1*8212SMichael.Corcoran@Sun.COM /* 2*8212SMichael.Corcoran@Sun.COM * CDDL HEADER START 3*8212SMichael.Corcoran@Sun.COM * 4*8212SMichael.Corcoran@Sun.COM * The contents of this file are subject to the terms of the 5*8212SMichael.Corcoran@Sun.COM * Common Development and Distribution License (the "License"). 6*8212SMichael.Corcoran@Sun.COM * You may not use this file except in compliance with the License. 7*8212SMichael.Corcoran@Sun.COM * 8*8212SMichael.Corcoran@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*8212SMichael.Corcoran@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*8212SMichael.Corcoran@Sun.COM * See the License for the specific language governing permissions 11*8212SMichael.Corcoran@Sun.COM * and limitations under the License. 12*8212SMichael.Corcoran@Sun.COM * 13*8212SMichael.Corcoran@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*8212SMichael.Corcoran@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*8212SMichael.Corcoran@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*8212SMichael.Corcoran@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*8212SMichael.Corcoran@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*8212SMichael.Corcoran@Sun.COM * 19*8212SMichael.Corcoran@Sun.COM * CDDL HEADER END 20*8212SMichael.Corcoran@Sun.COM */ 21*8212SMichael.Corcoran@Sun.COM /* 22*8212SMichael.Corcoran@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23*8212SMichael.Corcoran@Sun.COM * Use is subject to license terms. 24*8212SMichael.Corcoran@Sun.COM */ 25*8212SMichael.Corcoran@Sun.COM 26*8212SMichael.Corcoran@Sun.COM #include <sys/types.h> 27*8212SMichael.Corcoran@Sun.COM #include <sys/sysmacros.h> 28*8212SMichael.Corcoran@Sun.COM #include <sys/kmem.h> 29*8212SMichael.Corcoran@Sun.COM #include <sys/param.h> 30*8212SMichael.Corcoran@Sun.COM #include <sys/systm.h> 31*8212SMichael.Corcoran@Sun.COM #include <sys/errno.h> 32*8212SMichael.Corcoran@Sun.COM #include <sys/mman.h> 33*8212SMichael.Corcoran@Sun.COM #include <sys/cmn_err.h> 34*8212SMichael.Corcoran@Sun.COM #include <sys/cred.h> 35*8212SMichael.Corcoran@Sun.COM #include <sys/vmsystm.h> 36*8212SMichael.Corcoran@Sun.COM #include <sys/debug.h> 37*8212SMichael.Corcoran@Sun.COM #include <vm/as.h> 38*8212SMichael.Corcoran@Sun.COM #include <vm/seg.h> 39*8212SMichael.Corcoran@Sun.COM #include <sys/vmparam.h> 40*8212SMichael.Corcoran@Sun.COM #include <sys/vfs.h> 41*8212SMichael.Corcoran@Sun.COM #include <sys/elf.h> 42*8212SMichael.Corcoran@Sun.COM #include <sys/machelf.h> 43*8212SMichael.Corcoran@Sun.COM #include <sys/corectl.h> 44*8212SMichael.Corcoran@Sun.COM #include <sys/exec.h> 45*8212SMichael.Corcoran@Sun.COM #include <sys/exechdr.h> 46*8212SMichael.Corcoran@Sun.COM #include <sys/autoconf.h> 47*8212SMichael.Corcoran@Sun.COM #include <sys/mem.h> 48*8212SMichael.Corcoran@Sun.COM #include <vm/seg_dev.h> 49*8212SMichael.Corcoran@Sun.COM #include <sys/vmparam.h> 50*8212SMichael.Corcoran@Sun.COM #include <sys/mmapobj.h> 51*8212SMichael.Corcoran@Sun.COM #include <sys/atomic.h> 52*8212SMichael.Corcoran@Sun.COM 53*8212SMichael.Corcoran@Sun.COM /* 54*8212SMichael.Corcoran@Sun.COM * Theory statement: 55*8212SMichael.Corcoran@Sun.COM * 56*8212SMichael.Corcoran@Sun.COM * The main driving force behind mmapobj is to interpret and map ELF files 57*8212SMichael.Corcoran@Sun.COM * inside of the kernel instead of having the linker be responsible for this. 58*8212SMichael.Corcoran@Sun.COM * 59*8212SMichael.Corcoran@Sun.COM * mmapobj also supports the AOUT 4.x binary format as well as flat files in 60*8212SMichael.Corcoran@Sun.COM * a read only manner. 61*8212SMichael.Corcoran@Sun.COM * 62*8212SMichael.Corcoran@Sun.COM * When interpreting and mapping an ELF file, mmapobj will map each PT_LOAD 63*8212SMichael.Corcoran@Sun.COM * or PT_SUNWBSS segment according to the ELF standard. Refer to the "Linker 64*8212SMichael.Corcoran@Sun.COM * and Libraries Guide" for more information about the standard and mapping 65*8212SMichael.Corcoran@Sun.COM * rules. 66*8212SMichael.Corcoran@Sun.COM * 67*8212SMichael.Corcoran@Sun.COM * Having mmapobj interpret and map objects will allow the kernel to make the 68*8212SMichael.Corcoran@Sun.COM * best decision for where to place the mappings for said objects. Thus, we 69*8212SMichael.Corcoran@Sun.COM * can make optimizations inside of the kernel for specific platforms or 70*8212SMichael.Corcoran@Sun.COM * cache mapping information to make mapping objects faster. 71*8212SMichael.Corcoran@Sun.COM * 72*8212SMichael.Corcoran@Sun.COM * The lib_va_hash will be one such optimization. For each ELF object that 73*8212SMichael.Corcoran@Sun.COM * mmapobj is asked to interpret, we will attempt to cache the information 74*8212SMichael.Corcoran@Sun.COM * about the PT_LOAD and PT_SUNWBSS sections to speed up future mappings of 75*8212SMichael.Corcoran@Sun.COM * the same objects. We will cache up to LIBVA_CACHED_SEGS (see below) program 76*8212SMichael.Corcoran@Sun.COM * headers which should cover a majority of the libraries out there without 77*8212SMichael.Corcoran@Sun.COM * wasting space. In order to make sure that the cached information is valid, 78*8212SMichael.Corcoran@Sun.COM * we check the passed in vnode's mtime and ctime to make sure the vnode 79*8212SMichael.Corcoran@Sun.COM * has not been modified since the last time we used it. 80*8212SMichael.Corcoran@Sun.COM * 81*8212SMichael.Corcoran@Sun.COM * In addition, the lib_va_hash may contain a preferred starting VA for the 82*8212SMichael.Corcoran@Sun.COM * object which can be useful for platforms which support a shared context. 83*8212SMichael.Corcoran@Sun.COM * This will increase the likelyhood that library text can be shared among 84*8212SMichael.Corcoran@Sun.COM * many different processes. We limit the reserved VA space for 32 bit objects 85*8212SMichael.Corcoran@Sun.COM * in order to minimize fragmenting the processes address space. 86*8212SMichael.Corcoran@Sun.COM * 87*8212SMichael.Corcoran@Sun.COM * In addition to the above, the mmapobj interface allows for padding to be 88*8212SMichael.Corcoran@Sun.COM * requested before the first mapping and after the last mapping created. 89*8212SMichael.Corcoran@Sun.COM * When padding is requested, no additional optimizations will be made for 90*8212SMichael.Corcoran@Sun.COM * that request. 91*8212SMichael.Corcoran@Sun.COM */ 92*8212SMichael.Corcoran@Sun.COM 93*8212SMichael.Corcoran@Sun.COM /* 94*8212SMichael.Corcoran@Sun.COM * Threshold to prevent allocating too much kernel memory to read in the 95*8212SMichael.Corcoran@Sun.COM * program headers for an object. If it requires more than below, 96*8212SMichael.Corcoran@Sun.COM * we will use a KM_NOSLEEP allocation to allocate memory to hold all of the 97*8212SMichael.Corcoran@Sun.COM * program headers which could possibly fail. If less memory than below is 98*8212SMichael.Corcoran@Sun.COM * needed, then we use a KM_SLEEP allocation and are willing to wait for the 99*8212SMichael.Corcoran@Sun.COM * memory if we need to. 100*8212SMichael.Corcoran@Sun.COM */ 101*8212SMichael.Corcoran@Sun.COM size_t mmapobj_alloc_threshold = 65536; 102*8212SMichael.Corcoran@Sun.COM 103*8212SMichael.Corcoran@Sun.COM /* Debug stats for test coverage */ 104*8212SMichael.Corcoran@Sun.COM #ifdef DEBUG 105*8212SMichael.Corcoran@Sun.COM struct mobj_stats { 106*8212SMichael.Corcoran@Sun.COM uint_t mobjs_unmap_called; 107*8212SMichael.Corcoran@Sun.COM uint_t mobjs_remap_devnull; 108*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lookup_start; 109*8212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_start; 110*8212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_vmem; 111*8212SMichael.Corcoran@Sun.COM uint_t mobjs_add_collision; 112*8212SMichael.Corcoran@Sun.COM uint_t mobjs_get_addr; 113*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_flat_no_padding; 114*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_flat_padding; 115*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_text; 116*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_initdata; 117*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_preread; 118*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_text; 119*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_map_fail; 120*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_read_fail; 121*8212SMichael.Corcoran@Sun.COM uint_t mobjs_zfoddiff; 122*8212SMichael.Corcoran@Sun.COM uint_t mobjs_zfoddiff_nowrite; 123*8212SMichael.Corcoran@Sun.COM uint_t mobjs_zfodextra; 124*8212SMichael.Corcoran@Sun.COM uint_t mobjs_ptload_failed; 125*8212SMichael.Corcoran@Sun.COM uint_t mobjs_map_elf_no_holes; 126*8212SMichael.Corcoran@Sun.COM uint_t mobjs_unmap_hole; 127*8212SMichael.Corcoran@Sun.COM uint_t mobjs_nomem_header; 128*8212SMichael.Corcoran@Sun.COM uint_t mobjs_overlap_header; 129*8212SMichael.Corcoran@Sun.COM uint_t mobjs_np2_align; 130*8212SMichael.Corcoran@Sun.COM uint_t mobjs_np2_align_overflow; 131*8212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_padding; 132*8212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_mapped; 133*8212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_devnull; 134*8212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_in_use; 135*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_found; 136*8212SMichael.Corcoran@Sun.COM uint_t mobjs_no_loadable_yet; 137*8212SMichael.Corcoran@Sun.COM uint_t mobjs_nothing_to_map; 138*8212SMichael.Corcoran@Sun.COM uint_t mobjs_e2big; 139*8212SMichael.Corcoran@Sun.COM uint_t mobjs_dyn_pad_align; 140*8212SMichael.Corcoran@Sun.COM uint_t mobjs_dyn_pad_noalign; 141*8212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_start_fail; 142*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_nocache; 143*8212SMichael.Corcoran@Sun.COM uint_t mobjs_extra_padding; 144*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_not_needed; 145*8212SMichael.Corcoran@Sun.COM uint_t mobjs_no_mem_map_sz; 146*8212SMichael.Corcoran@Sun.COM uint_t mobjs_check_exec_failed; 147*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_used; 148*8212SMichael.Corcoran@Sun.COM uint_t mobjs_wrong_model; 149*8212SMichael.Corcoran@Sun.COM uint_t mobjs_noexec_fs; 150*8212SMichael.Corcoran@Sun.COM uint_t mobjs_e2big_et_rel; 151*8212SMichael.Corcoran@Sun.COM uint_t mobjs_et_rel_mapped; 152*8212SMichael.Corcoran@Sun.COM uint_t mobjs_unknown_elf_type; 153*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phent32_too_small; 154*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phent64_too_small; 155*8212SMichael.Corcoran@Sun.COM uint_t mobjs_inval_elf_class; 156*8212SMichael.Corcoran@Sun.COM uint_t mobjs_too_many_phdrs; 157*8212SMichael.Corcoran@Sun.COM uint_t mobjs_no_phsize; 158*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phsize_large; 159*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phsize_xtralarge; 160*8212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_wrong_model; 161*8212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_e2big; 162*8212SMichael.Corcoran@Sun.COM uint_t mobjs_fast; 163*8212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_success; 164*8212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_not_now; 165*8212SMichael.Corcoran@Sun.COM uint_t mobjs_small_file; 166*8212SMichael.Corcoran@Sun.COM uint_t mobjs_read_error; 167*8212SMichael.Corcoran@Sun.COM uint_t mobjs_unsupported; 168*8212SMichael.Corcoran@Sun.COM uint_t mobjs_flat_e2big; 169*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phent_align32; 170*8212SMichael.Corcoran@Sun.COM uint_t mobjs_phent_align64; 171*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_hit; 172*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_delay_delete; 173*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_delete; 174*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_add_delay_delete; 175*8212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_add_delete; 176*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 177*8212SMichael.Corcoran@Sun.COM uint_t mobjs_vac_align; 178*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_uzero_fault; 179*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_64bit_try; 180*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_noexec; 181*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_e2big; 182*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_lib; 183*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_fixed; 184*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_zfoddiff; 185*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_map_bss; 186*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_bss_fail; 187*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_nlist; 188*8212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_addr_in_use; 189*8212SMichael.Corcoran@Sun.COM #endif 190*8212SMichael.Corcoran@Sun.COM } mobj_stats; 191*8212SMichael.Corcoran@Sun.COM 192*8212SMichael.Corcoran@Sun.COM #define MOBJ_STAT_ADD(stat) ((mobj_stats.mobjs_##stat)++) 193*8212SMichael.Corcoran@Sun.COM #else 194*8212SMichael.Corcoran@Sun.COM #define MOBJ_STAT_ADD(stat) 195*8212SMichael.Corcoran@Sun.COM #endif 196*8212SMichael.Corcoran@Sun.COM 197*8212SMichael.Corcoran@Sun.COM /* lv_flags values - bitmap */ 198*8212SMichael.Corcoran@Sun.COM #define LV_ELF32 0x1 /* 32 bit ELF file */ 199*8212SMichael.Corcoran@Sun.COM #define LV_ELF64 0x2 /* 64 bit ELF file */ 200*8212SMichael.Corcoran@Sun.COM #define LV_DEL 0x4 /* delete when lv_refcnt hits zero */ 201*8212SMichael.Corcoran@Sun.COM 202*8212SMichael.Corcoran@Sun.COM /* 203*8212SMichael.Corcoran@Sun.COM * Note: lv_num_segs will denote how many segments this file has and will 204*8212SMichael.Corcoran@Sun.COM * only be set after the lv_mps array has been filled out. 205*8212SMichael.Corcoran@Sun.COM * lv_mps can only be valid if lv_num_segs is non-zero. 206*8212SMichael.Corcoran@Sun.COM */ 207*8212SMichael.Corcoran@Sun.COM struct lib_va { 208*8212SMichael.Corcoran@Sun.COM struct lib_va *lv_next; 209*8212SMichael.Corcoran@Sun.COM caddr_t lv_base_va; /* start va for library */ 210*8212SMichael.Corcoran@Sun.COM ssize_t lv_len; /* total va span of library */ 211*8212SMichael.Corcoran@Sun.COM size_t lv_align; /* minimum alignment */ 212*8212SMichael.Corcoran@Sun.COM uint64_t lv_nodeid; /* filesystem node id */ 213*8212SMichael.Corcoran@Sun.COM uint64_t lv_fsid; /* filesystem id */ 214*8212SMichael.Corcoran@Sun.COM timestruc_t lv_ctime; /* last time file was changed */ 215*8212SMichael.Corcoran@Sun.COM timestruc_t lv_mtime; /* or modified */ 216*8212SMichael.Corcoran@Sun.COM mmapobj_result_t lv_mps[LIBVA_CACHED_SEGS]; /* cached pheaders */ 217*8212SMichael.Corcoran@Sun.COM int lv_num_segs; /* # segs for this file */ 218*8212SMichael.Corcoran@Sun.COM int lv_flags; 219*8212SMichael.Corcoran@Sun.COM uint_t lv_refcnt; /* number of holds on struct */ 220*8212SMichael.Corcoran@Sun.COM }; 221*8212SMichael.Corcoran@Sun.COM 222*8212SMichael.Corcoran@Sun.COM #define LIB_VA_SIZE 1024 223*8212SMichael.Corcoran@Sun.COM #define LIB_VA_MASK (LIB_VA_SIZE - 1) 224*8212SMichael.Corcoran@Sun.COM #define LIB_VA_MUTEX_SHIFT 3 225*8212SMichael.Corcoran@Sun.COM 226*8212SMichael.Corcoran@Sun.COM #if (LIB_VA_SIZE & (LIB_VA_SIZE - 1)) 227*8212SMichael.Corcoran@Sun.COM #error "LIB_VA_SIZE is not a power of 2" 228*8212SMichael.Corcoran@Sun.COM #endif 229*8212SMichael.Corcoran@Sun.COM 230*8212SMichael.Corcoran@Sun.COM static struct lib_va *lib_va_hash[LIB_VA_SIZE]; 231*8212SMichael.Corcoran@Sun.COM static kmutex_t lib_va_hash_mutex[LIB_VA_SIZE >> LIB_VA_MUTEX_SHIFT]; 232*8212SMichael.Corcoran@Sun.COM 233*8212SMichael.Corcoran@Sun.COM #define LIB_VA_HASH_MUTEX(index) \ 234*8212SMichael.Corcoran@Sun.COM (&lib_va_hash_mutex[index >> LIB_VA_MUTEX_SHIFT]) 235*8212SMichael.Corcoran@Sun.COM 236*8212SMichael.Corcoran@Sun.COM #define LIB_VA_HASH(nodeid) \ 237*8212SMichael.Corcoran@Sun.COM (((nodeid) ^ ((nodeid) << 7) ^ ((nodeid) << 13)) & LIB_VA_MASK) 238*8212SMichael.Corcoran@Sun.COM 239*8212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH_ID(arg1, arg2) \ 240*8212SMichael.Corcoran@Sun.COM ((arg1)->lv_nodeid == (arg2)->va_nodeid && \ 241*8212SMichael.Corcoran@Sun.COM (arg1)->lv_fsid == (arg2)->va_fsid) 242*8212SMichael.Corcoran@Sun.COM 243*8212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH_TIME(arg1, arg2) \ 244*8212SMichael.Corcoran@Sun.COM ((arg1)->lv_ctime.tv_sec == (arg2)->va_ctime.tv_sec && \ 245*8212SMichael.Corcoran@Sun.COM (arg1)->lv_mtime.tv_sec == (arg2)->va_mtime.tv_sec && \ 246*8212SMichael.Corcoran@Sun.COM (arg1)->lv_ctime.tv_nsec == (arg2)->va_ctime.tv_nsec && \ 247*8212SMichael.Corcoran@Sun.COM (arg1)->lv_mtime.tv_nsec == (arg2)->va_mtime.tv_nsec) 248*8212SMichael.Corcoran@Sun.COM 249*8212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH(arg1, arg2) \ 250*8212SMichael.Corcoran@Sun.COM (LIB_VA_MATCH_ID(arg1, arg2) && LIB_VA_MATCH_TIME(arg1, arg2)) 251*8212SMichael.Corcoran@Sun.COM 252*8212SMichael.Corcoran@Sun.COM /* 253*8212SMichael.Corcoran@Sun.COM * In order to map libraries at the same VA in many processes, we need to carve 254*8212SMichael.Corcoran@Sun.COM * out our own address space for them which is unique across many processes. 255*8212SMichael.Corcoran@Sun.COM * We use different arenas for 32 bit and 64 bit libraries. 256*8212SMichael.Corcoran@Sun.COM * 257*8212SMichael.Corcoran@Sun.COM * Since the 32 bit address space is relatively small, we limit the number of 258*8212SMichael.Corcoran@Sun.COM * libraries which try to use consistent virtual addresses to lib_threshold. 259*8212SMichael.Corcoran@Sun.COM * For 64 bit libraries there is no such limit since the address space is large. 260*8212SMichael.Corcoran@Sun.COM */ 261*8212SMichael.Corcoran@Sun.COM static vmem_t *lib_va_32_arena; 262*8212SMichael.Corcoran@Sun.COM static vmem_t *lib_va_64_arena; 263*8212SMichael.Corcoran@Sun.COM uint_t lib_threshold = 20; /* modifiable via /etc/system */ 264*8212SMichael.Corcoran@Sun.COM 265*8212SMichael.Corcoran@Sun.COM /* 266*8212SMichael.Corcoran@Sun.COM * Number of 32 bit and 64 bit libraries in lib_va hash. 267*8212SMichael.Corcoran@Sun.COM */ 268*8212SMichael.Corcoran@Sun.COM static uint_t libs_mapped_32 = 0; 269*8212SMichael.Corcoran@Sun.COM static uint_t libs_mapped_64 = 0; 270*8212SMichael.Corcoran@Sun.COM 271*8212SMichael.Corcoran@Sun.COM /* 272*8212SMichael.Corcoran@Sun.COM * Initialize the VA span of the lib_va arenas to about half of the VA space 273*8212SMichael.Corcoran@Sun.COM * of a user process. These VAs will be used for optimized allocation of 274*8212SMichael.Corcoran@Sun.COM * libraries, such that subsequent mappings of the same library will attempt 275*8212SMichael.Corcoran@Sun.COM * to use the same VA as previous mappings of that library. 276*8212SMichael.Corcoran@Sun.COM */ 277*8212SMichael.Corcoran@Sun.COM void 278*8212SMichael.Corcoran@Sun.COM lib_va_init(void) 279*8212SMichael.Corcoran@Sun.COM { 280*8212SMichael.Corcoran@Sun.COM size_t start; 281*8212SMichael.Corcoran@Sun.COM size_t end; 282*8212SMichael.Corcoran@Sun.COM size_t len; 283*8212SMichael.Corcoran@Sun.COM /* 284*8212SMichael.Corcoran@Sun.COM * On 32 bit sparc, the user stack and /lib/ld.so.1 will both live 285*8212SMichael.Corcoran@Sun.COM * above the end address that we choose. On 32bit x86 only 286*8212SMichael.Corcoran@Sun.COM * /lib/ld.so.1 will live above the end address that we choose 287*8212SMichael.Corcoran@Sun.COM * because the user stack is at the bottom of the address space. 288*8212SMichael.Corcoran@Sun.COM * 289*8212SMichael.Corcoran@Sun.COM * We estimate the size of ld.so.1 to be 512K which leaves significant 290*8212SMichael.Corcoran@Sun.COM * room for growth without needing to change this value. Thus it is 291*8212SMichael.Corcoran@Sun.COM * safe for libraries to be mapped up to that address. 292*8212SMichael.Corcoran@Sun.COM * 293*8212SMichael.Corcoran@Sun.COM * If the length of ld.so.1 were to grow beyond 512K then 294*8212SMichael.Corcoran@Sun.COM * a library who has a reserved address in that range would always 295*8212SMichael.Corcoran@Sun.COM * fail to get that address and would have to call map_addr 296*8212SMichael.Corcoran@Sun.COM * to get an unused address range. On DEBUG kernels, we will check 297*8212SMichael.Corcoran@Sun.COM * on the first use of lib_va that our address does not overlap 298*8212SMichael.Corcoran@Sun.COM * ld.so.1, and if it does, then we'll print a cmn_err message. 299*8212SMichael.Corcoran@Sun.COM */ 300*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 301*8212SMichael.Corcoran@Sun.COM end = _userlimit32 - DFLSSIZ - (512 * 1024); 302*8212SMichael.Corcoran@Sun.COM #elif defined(__i386) || defined(__amd64) 303*8212SMichael.Corcoran@Sun.COM end = _userlimit32 - (512 * 1024); 304*8212SMichael.Corcoran@Sun.COM #else 305*8212SMichael.Corcoran@Sun.COM #error "no recognized machine type is defined" 306*8212SMichael.Corcoran@Sun.COM #endif 307*8212SMichael.Corcoran@Sun.COM len = end >> 1; 308*8212SMichael.Corcoran@Sun.COM len = P2ROUNDUP(len, PAGESIZE); 309*8212SMichael.Corcoran@Sun.COM start = end - len; 310*8212SMichael.Corcoran@Sun.COM lib_va_32_arena = vmem_create("lib_va_32", (void *)start, len, 311*8212SMichael.Corcoran@Sun.COM PAGESIZE, NULL, NULL, NULL, 0, VM_NOSLEEP | VMC_IDENTIFIER); 312*8212SMichael.Corcoran@Sun.COM 313*8212SMichael.Corcoran@Sun.COM #if defined(_LP64) 314*8212SMichael.Corcoran@Sun.COM /* 315*8212SMichael.Corcoran@Sun.COM * The user stack and /lib/ld.so.1 will both live above the end address 316*8212SMichael.Corcoran@Sun.COM * that we choose. We estimate the size of a mapped ld.so.1 to be 2M 317*8212SMichael.Corcoran@Sun.COM * which leaves significant room for growth without needing to change 318*8212SMichael.Corcoran@Sun.COM * this value. Thus it is safe for libraries to be mapped up to 319*8212SMichael.Corcoran@Sun.COM * that address. The same considerations for the size of ld.so.1 that 320*8212SMichael.Corcoran@Sun.COM * were mentioned above also apply here. 321*8212SMichael.Corcoran@Sun.COM */ 322*8212SMichael.Corcoran@Sun.COM end = _userlimit - DFLSSIZ - (2 * 1024 * 1024); 323*8212SMichael.Corcoran@Sun.COM len = end >> 1; 324*8212SMichael.Corcoran@Sun.COM len = P2ROUNDUP(len, PAGESIZE); 325*8212SMichael.Corcoran@Sun.COM start = end - len; 326*8212SMichael.Corcoran@Sun.COM lib_va_64_arena = vmem_create("lib_va_64", (void *)start, len, 327*8212SMichael.Corcoran@Sun.COM PAGESIZE, NULL, NULL, NULL, 0, VM_NOSLEEP | VMC_IDENTIFIER); 328*8212SMichael.Corcoran@Sun.COM #endif 329*8212SMichael.Corcoran@Sun.COM } 330*8212SMichael.Corcoran@Sun.COM 331*8212SMichael.Corcoran@Sun.COM /* 332*8212SMichael.Corcoran@Sun.COM * Free up the resources associated with lvp as well as lvp itself. 333*8212SMichael.Corcoran@Sun.COM * We also decrement the number of libraries mapped via a lib_va 334*8212SMichael.Corcoran@Sun.COM * cached virtual address. 335*8212SMichael.Corcoran@Sun.COM */ 336*8212SMichael.Corcoran@Sun.COM void 337*8212SMichael.Corcoran@Sun.COM lib_va_free(struct lib_va *lvp) 338*8212SMichael.Corcoran@Sun.COM { 339*8212SMichael.Corcoran@Sun.COM int is_64bit = lvp->lv_flags & LV_ELF64; 340*8212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_refcnt == 0); 341*8212SMichael.Corcoran@Sun.COM 342*8212SMichael.Corcoran@Sun.COM if (lvp->lv_base_va != NULL) { 343*8212SMichael.Corcoran@Sun.COM vmem_xfree(is_64bit ? lib_va_64_arena : lib_va_32_arena, 344*8212SMichael.Corcoran@Sun.COM lvp->lv_base_va, lvp->lv_len); 345*8212SMichael.Corcoran@Sun.COM if (is_64bit) { 346*8212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_64, -1); 347*8212SMichael.Corcoran@Sun.COM } else { 348*8212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_32, -1); 349*8212SMichael.Corcoran@Sun.COM } 350*8212SMichael.Corcoran@Sun.COM } 351*8212SMichael.Corcoran@Sun.COM kmem_free(lvp, sizeof (struct lib_va)); 352*8212SMichael.Corcoran@Sun.COM } 353*8212SMichael.Corcoran@Sun.COM 354*8212SMichael.Corcoran@Sun.COM /* 355*8212SMichael.Corcoran@Sun.COM * See if the file associated with the vap passed in is in the lib_va hash. 356*8212SMichael.Corcoran@Sun.COM * If it is and the file has not been modified since last use, then 357*8212SMichael.Corcoran@Sun.COM * return a pointer to that data. Otherwise, return NULL if the file has 358*8212SMichael.Corcoran@Sun.COM * changed or the file was not found in the hash. 359*8212SMichael.Corcoran@Sun.COM */ 360*8212SMichael.Corcoran@Sun.COM static struct lib_va * 361*8212SMichael.Corcoran@Sun.COM lib_va_find(vattr_t *vap) 362*8212SMichael.Corcoran@Sun.COM { 363*8212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 364*8212SMichael.Corcoran@Sun.COM struct lib_va *del = NULL; 365*8212SMichael.Corcoran@Sun.COM struct lib_va **tmp; 366*8212SMichael.Corcoran@Sun.COM uint_t index; 367*8212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(vap->va_nodeid); 368*8212SMichael.Corcoran@Sun.COM 369*8212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 370*8212SMichael.Corcoran@Sun.COM tmp = &lib_va_hash[index]; 371*8212SMichael.Corcoran@Sun.COM while (*tmp != NULL) { 372*8212SMichael.Corcoran@Sun.COM lvp = *tmp; 373*8212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_ID(lvp, vap)) { 374*8212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_TIME(lvp, vap)) { 375*8212SMichael.Corcoran@Sun.COM ASSERT((lvp->lv_flags & LV_DEL) == 0); 376*8212SMichael.Corcoran@Sun.COM lvp->lv_refcnt++; 377*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_hit); 378*8212SMichael.Corcoran@Sun.COM } else { 379*8212SMichael.Corcoran@Sun.COM /* 380*8212SMichael.Corcoran@Sun.COM * file was updated since last use. 381*8212SMichael.Corcoran@Sun.COM * need to remove it from list. 382*8212SMichael.Corcoran@Sun.COM */ 383*8212SMichael.Corcoran@Sun.COM del = lvp; 384*8212SMichael.Corcoran@Sun.COM *tmp = del->lv_next; 385*8212SMichael.Corcoran@Sun.COM del->lv_next = NULL; 386*8212SMichael.Corcoran@Sun.COM /* 387*8212SMichael.Corcoran@Sun.COM * If we can't delete it now, mark it for later 388*8212SMichael.Corcoran@Sun.COM */ 389*8212SMichael.Corcoran@Sun.COM if (del->lv_refcnt) { 390*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_delay_delete); 391*8212SMichael.Corcoran@Sun.COM del->lv_flags |= LV_DEL; 392*8212SMichael.Corcoran@Sun.COM del = NULL; 393*8212SMichael.Corcoran@Sun.COM } 394*8212SMichael.Corcoran@Sun.COM lvp = NULL; 395*8212SMichael.Corcoran@Sun.COM } 396*8212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 397*8212SMichael.Corcoran@Sun.COM if (del) { 398*8212SMichael.Corcoran@Sun.COM ASSERT(del->lv_refcnt == 0); 399*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_delete); 400*8212SMichael.Corcoran@Sun.COM lib_va_free(del); 401*8212SMichael.Corcoran@Sun.COM } 402*8212SMichael.Corcoran@Sun.COM return (lvp); 403*8212SMichael.Corcoran@Sun.COM } 404*8212SMichael.Corcoran@Sun.COM tmp = &lvp->lv_next; 405*8212SMichael.Corcoran@Sun.COM } 406*8212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 407*8212SMichael.Corcoran@Sun.COM return (NULL); 408*8212SMichael.Corcoran@Sun.COM } 409*8212SMichael.Corcoran@Sun.COM 410*8212SMichael.Corcoran@Sun.COM /* 411*8212SMichael.Corcoran@Sun.COM * Add a new entry to the lib_va hash. 412*8212SMichael.Corcoran@Sun.COM * Search the hash while holding the appropriate mutex to make sure that the 413*8212SMichael.Corcoran@Sun.COM * data is not already in the cache. If we find data that is in the cache 414*8212SMichael.Corcoran@Sun.COM * already and has not been modified since last use, we return NULL. If it 415*8212SMichael.Corcoran@Sun.COM * has been modified since last use, we will remove that entry from 416*8212SMichael.Corcoran@Sun.COM * the hash and it will be deleted once it's reference count reaches zero. 417*8212SMichael.Corcoran@Sun.COM * If there is no current entry in the hash we will add the new entry and 418*8212SMichael.Corcoran@Sun.COM * return it to the caller who is responsible for calling lib_va_release to 419*8212SMichael.Corcoran@Sun.COM * drop their reference count on it. 420*8212SMichael.Corcoran@Sun.COM * 421*8212SMichael.Corcoran@Sun.COM * lv_num_segs will be set to zero since the caller needs to add that 422*8212SMichael.Corcoran@Sun.COM * information to the data structure. 423*8212SMichael.Corcoran@Sun.COM */ 424*8212SMichael.Corcoran@Sun.COM static struct lib_va * 425*8212SMichael.Corcoran@Sun.COM lib_va_add_hash(caddr_t base_va, ssize_t len, size_t align, vattr_t *vap) 426*8212SMichael.Corcoran@Sun.COM { 427*8212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 428*8212SMichael.Corcoran@Sun.COM uint_t index; 429*8212SMichael.Corcoran@Sun.COM model_t model; 430*8212SMichael.Corcoran@Sun.COM struct lib_va **tmp; 431*8212SMichael.Corcoran@Sun.COM struct lib_va *del = NULL; 432*8212SMichael.Corcoran@Sun.COM 433*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 434*8212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(vap->va_nodeid); 435*8212SMichael.Corcoran@Sun.COM 436*8212SMichael.Corcoran@Sun.COM lvp = kmem_alloc(sizeof (struct lib_va), KM_SLEEP); 437*8212SMichael.Corcoran@Sun.COM 438*8212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 439*8212SMichael.Corcoran@Sun.COM 440*8212SMichael.Corcoran@Sun.COM /* 441*8212SMichael.Corcoran@Sun.COM * Make sure not adding same data a second time. 442*8212SMichael.Corcoran@Sun.COM * The hash chains should be relatively short and adding 443*8212SMichael.Corcoran@Sun.COM * is a relatively rare event, so it's worth the check. 444*8212SMichael.Corcoran@Sun.COM */ 445*8212SMichael.Corcoran@Sun.COM tmp = &lib_va_hash[index]; 446*8212SMichael.Corcoran@Sun.COM while (*tmp != NULL) { 447*8212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_ID(*tmp, vap)) { 448*8212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_TIME(*tmp, vap)) { 449*8212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 450*8212SMichael.Corcoran@Sun.COM kmem_free(lvp, sizeof (struct lib_va)); 451*8212SMichael.Corcoran@Sun.COM return (NULL); 452*8212SMichael.Corcoran@Sun.COM } 453*8212SMichael.Corcoran@Sun.COM 454*8212SMichael.Corcoran@Sun.COM /* 455*8212SMichael.Corcoran@Sun.COM * We have the same nodeid and fsid but the file has 456*8212SMichael.Corcoran@Sun.COM * been modified since we last saw it. 457*8212SMichael.Corcoran@Sun.COM * Need to remove the old node and add this new 458*8212SMichael.Corcoran@Sun.COM * one. 459*8212SMichael.Corcoran@Sun.COM * Could probably use a callback mechanism to make 460*8212SMichael.Corcoran@Sun.COM * this cleaner. 461*8212SMichael.Corcoran@Sun.COM */ 462*8212SMichael.Corcoran@Sun.COM ASSERT(del == NULL); 463*8212SMichael.Corcoran@Sun.COM del = *tmp; 464*8212SMichael.Corcoran@Sun.COM *tmp = del->lv_next; 465*8212SMichael.Corcoran@Sun.COM del->lv_next = NULL; 466*8212SMichael.Corcoran@Sun.COM 467*8212SMichael.Corcoran@Sun.COM /* 468*8212SMichael.Corcoran@Sun.COM * Check to see if we can free it. If lv_refcnt 469*8212SMichael.Corcoran@Sun.COM * is greater than zero, than some other thread 470*8212SMichael.Corcoran@Sun.COM * has a reference to the one we want to delete 471*8212SMichael.Corcoran@Sun.COM * and we can not delete it. All of this is done 472*8212SMichael.Corcoran@Sun.COM * under the lib_va_hash_mutex lock so it is atomic. 473*8212SMichael.Corcoran@Sun.COM */ 474*8212SMichael.Corcoran@Sun.COM if (del->lv_refcnt) { 475*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_add_delay_delete); 476*8212SMichael.Corcoran@Sun.COM del->lv_flags |= LV_DEL; 477*8212SMichael.Corcoran@Sun.COM del = NULL; 478*8212SMichael.Corcoran@Sun.COM } 479*8212SMichael.Corcoran@Sun.COM /* tmp is already advanced */ 480*8212SMichael.Corcoran@Sun.COM continue; 481*8212SMichael.Corcoran@Sun.COM } 482*8212SMichael.Corcoran@Sun.COM tmp = &((*tmp)->lv_next); 483*8212SMichael.Corcoran@Sun.COM } 484*8212SMichael.Corcoran@Sun.COM 485*8212SMichael.Corcoran@Sun.COM lvp->lv_base_va = base_va; 486*8212SMichael.Corcoran@Sun.COM lvp->lv_len = len; 487*8212SMichael.Corcoran@Sun.COM lvp->lv_align = align; 488*8212SMichael.Corcoran@Sun.COM lvp->lv_nodeid = vap->va_nodeid; 489*8212SMichael.Corcoran@Sun.COM lvp->lv_fsid = vap->va_fsid; 490*8212SMichael.Corcoran@Sun.COM lvp->lv_ctime.tv_sec = vap->va_ctime.tv_sec; 491*8212SMichael.Corcoran@Sun.COM lvp->lv_ctime.tv_nsec = vap->va_ctime.tv_nsec; 492*8212SMichael.Corcoran@Sun.COM lvp->lv_mtime.tv_sec = vap->va_mtime.tv_sec; 493*8212SMichael.Corcoran@Sun.COM lvp->lv_mtime.tv_nsec = vap->va_mtime.tv_nsec; 494*8212SMichael.Corcoran@Sun.COM lvp->lv_next = NULL; 495*8212SMichael.Corcoran@Sun.COM lvp->lv_refcnt = 1; 496*8212SMichael.Corcoran@Sun.COM 497*8212SMichael.Corcoran@Sun.COM /* Caller responsible for filling this and lv_mps out */ 498*8212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = 0; 499*8212SMichael.Corcoran@Sun.COM 500*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 501*8212SMichael.Corcoran@Sun.COM lvp->lv_flags = LV_ELF64; 502*8212SMichael.Corcoran@Sun.COM } else { 503*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 504*8212SMichael.Corcoran@Sun.COM lvp->lv_flags = LV_ELF32; 505*8212SMichael.Corcoran@Sun.COM } 506*8212SMichael.Corcoran@Sun.COM 507*8212SMichael.Corcoran@Sun.COM if (base_va != NULL) { 508*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 509*8212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_64, 1); 510*8212SMichael.Corcoran@Sun.COM } else { 511*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 512*8212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_32, 1); 513*8212SMichael.Corcoran@Sun.COM } 514*8212SMichael.Corcoran@Sun.COM } 515*8212SMichael.Corcoran@Sun.COM ASSERT(*tmp == NULL); 516*8212SMichael.Corcoran@Sun.COM *tmp = lvp; 517*8212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 518*8212SMichael.Corcoran@Sun.COM if (del) { 519*8212SMichael.Corcoran@Sun.COM ASSERT(del->lv_refcnt == 0); 520*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_add_delete); 521*8212SMichael.Corcoran@Sun.COM lib_va_free(del); 522*8212SMichael.Corcoran@Sun.COM } 523*8212SMichael.Corcoran@Sun.COM return (lvp); 524*8212SMichael.Corcoran@Sun.COM } 525*8212SMichael.Corcoran@Sun.COM 526*8212SMichael.Corcoran@Sun.COM /* 527*8212SMichael.Corcoran@Sun.COM * Release the hold on lvp which was acquired by lib_va_find or lib_va_add_hash. 528*8212SMichael.Corcoran@Sun.COM * In addition, if this is the last hold and lvp is marked for deletion, 529*8212SMichael.Corcoran@Sun.COM * free up it's reserved address space and free the structure. 530*8212SMichael.Corcoran@Sun.COM */ 531*8212SMichael.Corcoran@Sun.COM static void 532*8212SMichael.Corcoran@Sun.COM lib_va_release(struct lib_va *lvp) 533*8212SMichael.Corcoran@Sun.COM { 534*8212SMichael.Corcoran@Sun.COM uint_t index; 535*8212SMichael.Corcoran@Sun.COM int to_del = 0; 536*8212SMichael.Corcoran@Sun.COM 537*8212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_refcnt > 0); 538*8212SMichael.Corcoran@Sun.COM 539*8212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(lvp->lv_nodeid); 540*8212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 541*8212SMichael.Corcoran@Sun.COM if (--lvp->lv_refcnt == 0 && (lvp->lv_flags & LV_DEL)) { 542*8212SMichael.Corcoran@Sun.COM to_del = 1; 543*8212SMichael.Corcoran@Sun.COM } 544*8212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 545*8212SMichael.Corcoran@Sun.COM if (to_del) { 546*8212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_next == 0); 547*8212SMichael.Corcoran@Sun.COM lib_va_free(lvp); 548*8212SMichael.Corcoran@Sun.COM } 549*8212SMichael.Corcoran@Sun.COM } 550*8212SMichael.Corcoran@Sun.COM 551*8212SMichael.Corcoran@Sun.COM /* 552*8212SMichael.Corcoran@Sun.COM * Dummy function for mapping through /dev/null 553*8212SMichael.Corcoran@Sun.COM * Normally I would have used mmmmap in common/io/mem.c 554*8212SMichael.Corcoran@Sun.COM * but that is a static function, and for /dev/null, it 555*8212SMichael.Corcoran@Sun.COM * just returns -1. 556*8212SMichael.Corcoran@Sun.COM */ 557*8212SMichael.Corcoran@Sun.COM /* ARGSUSED */ 558*8212SMichael.Corcoran@Sun.COM static int 559*8212SMichael.Corcoran@Sun.COM mmapobj_dummy(dev_t dev, off_t off, int prot) 560*8212SMichael.Corcoran@Sun.COM { 561*8212SMichael.Corcoran@Sun.COM return (-1); 562*8212SMichael.Corcoran@Sun.COM } 563*8212SMichael.Corcoran@Sun.COM 564*8212SMichael.Corcoran@Sun.COM /* 565*8212SMichael.Corcoran@Sun.COM * Called when an error occurred which requires mmapobj to return failure. 566*8212SMichael.Corcoran@Sun.COM * All mapped objects will be unmapped and /dev/null mappings will be 567*8212SMichael.Corcoran@Sun.COM * reclaimed if necessary. 568*8212SMichael.Corcoran@Sun.COM * num_mapped is the number of elements of mrp which have been mapped, and 569*8212SMichael.Corcoran@Sun.COM * num_segs is the total number of elements in mrp. 570*8212SMichael.Corcoran@Sun.COM * For e_type ET_EXEC, we need to unmap all of the elements in mrp since 571*8212SMichael.Corcoran@Sun.COM * we had already made reservations for them. 572*8212SMichael.Corcoran@Sun.COM * If num_mapped equals num_segs, then we know that we had fully mapped 573*8212SMichael.Corcoran@Sun.COM * the file and only need to clean up the segments described. 574*8212SMichael.Corcoran@Sun.COM * If they are not equal, then for ET_DYN we will unmap the range from the 575*8212SMichael.Corcoran@Sun.COM * end of the last mapped segment to the end of the last segment in mrp 576*8212SMichael.Corcoran@Sun.COM * since we would have made a reservation for that memory earlier. 577*8212SMichael.Corcoran@Sun.COM * If e_type is passed in as zero, num_mapped must equal num_segs. 578*8212SMichael.Corcoran@Sun.COM */ 579*8212SMichael.Corcoran@Sun.COM void 580*8212SMichael.Corcoran@Sun.COM mmapobj_unmap(mmapobj_result_t *mrp, int num_mapped, int num_segs, 581*8212SMichael.Corcoran@Sun.COM ushort_t e_type) 582*8212SMichael.Corcoran@Sun.COM { 583*8212SMichael.Corcoran@Sun.COM int i; 584*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 585*8212SMichael.Corcoran@Sun.COM caddr_t addr; 586*8212SMichael.Corcoran@Sun.COM size_t size; 587*8212SMichael.Corcoran@Sun.COM 588*8212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 589*8212SMichael.Corcoran@Sun.COM num_mapped = num_segs; 590*8212SMichael.Corcoran@Sun.COM } 591*8212SMichael.Corcoran@Sun.COM #ifdef DEBUG 592*8212SMichael.Corcoran@Sun.COM if (e_type == 0) { 593*8212SMichael.Corcoran@Sun.COM ASSERT(num_mapped == num_segs); 594*8212SMichael.Corcoran@Sun.COM } 595*8212SMichael.Corcoran@Sun.COM #endif 596*8212SMichael.Corcoran@Sun.COM 597*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unmap_called); 598*8212SMichael.Corcoran@Sun.COM for (i = 0; i < num_mapped; i++) { 599*8212SMichael.Corcoran@Sun.COM 600*8212SMichael.Corcoran@Sun.COM /* 601*8212SMichael.Corcoran@Sun.COM * If we are going to have to create a mapping we need to 602*8212SMichael.Corcoran@Sun.COM * make sure that no one else will use the address we 603*8212SMichael.Corcoran@Sun.COM * need to remap between the time it is unmapped and 604*8212SMichael.Corcoran@Sun.COM * mapped below. 605*8212SMichael.Corcoran@Sun.COM */ 606*8212SMichael.Corcoran@Sun.COM if (mrp[i].mr_flags & MR_RESV) { 607*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 608*8212SMichael.Corcoran@Sun.COM } 609*8212SMichael.Corcoran@Sun.COM /* Always need to unmap what we mapped */ 610*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, mrp[i].mr_addr, mrp[i].mr_msize); 611*8212SMichael.Corcoran@Sun.COM 612*8212SMichael.Corcoran@Sun.COM /* Need to reclaim /dev/null reservation from earlier */ 613*8212SMichael.Corcoran@Sun.COM if (mrp[i].mr_flags & MR_RESV) { 614*8212SMichael.Corcoran@Sun.COM struct segdev_crargs dev_a; 615*8212SMichael.Corcoran@Sun.COM 616*8212SMichael.Corcoran@Sun.COM ASSERT(e_type != ET_DYN); 617*8212SMichael.Corcoran@Sun.COM /* 618*8212SMichael.Corcoran@Sun.COM * Use seg_dev segment driver for /dev/null mapping. 619*8212SMichael.Corcoran@Sun.COM */ 620*8212SMichael.Corcoran@Sun.COM dev_a.mapfunc = mmapobj_dummy; 621*8212SMichael.Corcoran@Sun.COM dev_a.dev = makedevice(mm_major, M_NULL); 622*8212SMichael.Corcoran@Sun.COM dev_a.offset = 0; 623*8212SMichael.Corcoran@Sun.COM dev_a.type = 0; /* neither PRIVATE nor SHARED */ 624*8212SMichael.Corcoran@Sun.COM dev_a.prot = dev_a.maxprot = (uchar_t)PROT_NONE; 625*8212SMichael.Corcoran@Sun.COM dev_a.hat_attr = 0; 626*8212SMichael.Corcoran@Sun.COM dev_a.hat_flags = 0; 627*8212SMichael.Corcoran@Sun.COM 628*8212SMichael.Corcoran@Sun.COM (void) as_map(as, mrp[i].mr_addr, mrp[i].mr_msize, 629*8212SMichael.Corcoran@Sun.COM segdev_create, &dev_a); 630*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(remap_devnull); 631*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 632*8212SMichael.Corcoran@Sun.COM } 633*8212SMichael.Corcoran@Sun.COM } 634*8212SMichael.Corcoran@Sun.COM 635*8212SMichael.Corcoran@Sun.COM if (num_mapped != num_segs) { 636*8212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_DYN); 637*8212SMichael.Corcoran@Sun.COM /* Need to unmap any reservation made after last mapped seg */ 638*8212SMichael.Corcoran@Sun.COM if (num_mapped == 0) { 639*8212SMichael.Corcoran@Sun.COM addr = mrp[0].mr_addr; 640*8212SMichael.Corcoran@Sun.COM } else { 641*8212SMichael.Corcoran@Sun.COM addr = mrp[num_mapped - 1].mr_addr + 642*8212SMichael.Corcoran@Sun.COM mrp[num_mapped - 1].mr_msize; 643*8212SMichael.Corcoran@Sun.COM } 644*8212SMichael.Corcoran@Sun.COM size = (size_t)mrp[num_segs - 1].mr_addr + 645*8212SMichael.Corcoran@Sun.COM mrp[num_segs - 1].mr_msize - (size_t)addr; 646*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 647*8212SMichael.Corcoran@Sun.COM 648*8212SMichael.Corcoran@Sun.COM /* 649*8212SMichael.Corcoran@Sun.COM * Now we need to unmap the holes between mapped segs. 650*8212SMichael.Corcoran@Sun.COM * Note that we have not mapped all of the segments and thus 651*8212SMichael.Corcoran@Sun.COM * the holes between segments would not have been unmapped 652*8212SMichael.Corcoran@Sun.COM * yet. If num_mapped == num_segs, then all of the holes 653*8212SMichael.Corcoran@Sun.COM * between segments would have already been unmapped. 654*8212SMichael.Corcoran@Sun.COM */ 655*8212SMichael.Corcoran@Sun.COM 656*8212SMichael.Corcoran@Sun.COM for (i = 1; i < num_mapped; i++) { 657*8212SMichael.Corcoran@Sun.COM addr = mrp[i - 1].mr_addr + mrp[i - 1].mr_msize; 658*8212SMichael.Corcoran@Sun.COM size = mrp[i].mr_addr - addr; 659*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 660*8212SMichael.Corcoran@Sun.COM } 661*8212SMichael.Corcoran@Sun.COM } 662*8212SMichael.Corcoran@Sun.COM } 663*8212SMichael.Corcoran@Sun.COM 664*8212SMichael.Corcoran@Sun.COM /* 665*8212SMichael.Corcoran@Sun.COM * We need to add the start address into mrp so that the unmap function 666*8212SMichael.Corcoran@Sun.COM * has absolute addresses to use. 667*8212SMichael.Corcoran@Sun.COM */ 668*8212SMichael.Corcoran@Sun.COM static void 669*8212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mmapobj_result_t *mrp, int num_mapped, caddr_t start_addr) 670*8212SMichael.Corcoran@Sun.COM { 671*8212SMichael.Corcoran@Sun.COM int i; 672*8212SMichael.Corcoran@Sun.COM 673*8212SMichael.Corcoran@Sun.COM for (i = 0; i < num_mapped; i++) { 674*8212SMichael.Corcoran@Sun.COM mrp[i].mr_addr += (size_t)start_addr; 675*8212SMichael.Corcoran@Sun.COM } 676*8212SMichael.Corcoran@Sun.COM mmapobj_unmap(mrp, num_mapped, num_mapped, ET_EXEC); 677*8212SMichael.Corcoran@Sun.COM } 678*8212SMichael.Corcoran@Sun.COM 679*8212SMichael.Corcoran@Sun.COM static caddr_t 680*8212SMichael.Corcoran@Sun.COM mmapobj_lookup_start_addr(struct lib_va *lvp) 681*8212SMichael.Corcoran@Sun.COM { 682*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 683*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 684*8212SMichael.Corcoran@Sun.COM int error; 685*8212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 686*8212SMichael.Corcoran@Sun.COM caddr_t base = NULL; 687*8212SMichael.Corcoran@Sun.COM size_t len; 688*8212SMichael.Corcoran@Sun.COM size_t align; 689*8212SMichael.Corcoran@Sun.COM 690*8212SMichael.Corcoran@Sun.COM ASSERT(lvp != NULL); 691*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lookup_start); 692*8212SMichael.Corcoran@Sun.COM 693*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 694*8212SMichael.Corcoran@Sun.COM 695*8212SMichael.Corcoran@Sun.COM base = lvp->lv_base_va; 696*8212SMichael.Corcoran@Sun.COM len = lvp->lv_len; 697*8212SMichael.Corcoran@Sun.COM 698*8212SMichael.Corcoran@Sun.COM /* 699*8212SMichael.Corcoran@Sun.COM * If we don't have an expected base address, or the one that we want 700*8212SMichael.Corcoran@Sun.COM * to use is not available, go get an acceptable address range. 701*8212SMichael.Corcoran@Sun.COM */ 702*8212SMichael.Corcoran@Sun.COM if (base == NULL || as_gap(as, len, &base, &len, 0, NULL)) { 703*8212SMichael.Corcoran@Sun.COM 704*8212SMichael.Corcoran@Sun.COM if (lvp->lv_flags & LV_ELF64) { 705*8212SMichael.Corcoran@Sun.COM ma_flags = 0; 706*8212SMichael.Corcoran@Sun.COM } 707*8212SMichael.Corcoran@Sun.COM 708*8212SMichael.Corcoran@Sun.COM align = lvp->lv_align; 709*8212SMichael.Corcoran@Sun.COM if (align > 1) { 710*8212SMichael.Corcoran@Sun.COM ma_flags |= MAP_ALIGN; 711*8212SMichael.Corcoran@Sun.COM } 712*8212SMichael.Corcoran@Sun.COM 713*8212SMichael.Corcoran@Sun.COM base = (caddr_t)align; 714*8212SMichael.Corcoran@Sun.COM map_addr(&base, len, 0, 1, ma_flags); 715*8212SMichael.Corcoran@Sun.COM } 716*8212SMichael.Corcoran@Sun.COM 717*8212SMichael.Corcoran@Sun.COM /* 718*8212SMichael.Corcoran@Sun.COM * Need to reserve the address space we're going to use. 719*8212SMichael.Corcoran@Sun.COM * Don't reserve swap space since we'll be mapping over this. 720*8212SMichael.Corcoran@Sun.COM */ 721*8212SMichael.Corcoran@Sun.COM if (base != NULL) { 722*8212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 723*8212SMichael.Corcoran@Sun.COM error = as_map(as, base, len, segvn_create, &crargs); 724*8212SMichael.Corcoran@Sun.COM if (error) { 725*8212SMichael.Corcoran@Sun.COM base = NULL; 726*8212SMichael.Corcoran@Sun.COM } 727*8212SMichael.Corcoran@Sun.COM } 728*8212SMichael.Corcoran@Sun.COM 729*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 730*8212SMichael.Corcoran@Sun.COM return (base); 731*8212SMichael.Corcoran@Sun.COM } 732*8212SMichael.Corcoran@Sun.COM 733*8212SMichael.Corcoran@Sun.COM /* 734*8212SMichael.Corcoran@Sun.COM * Get the starting address for a given file to be mapped and return it 735*8212SMichael.Corcoran@Sun.COM * to the caller. If we're using lib_va and we need to allocate an address, 736*8212SMichael.Corcoran@Sun.COM * we will attempt to allocate it from the global reserved pool such that the 737*8212SMichael.Corcoran@Sun.COM * same address can be used in the future for this file. If we can't use the 738*8212SMichael.Corcoran@Sun.COM * reserved address then we just get one that will fit in our address space. 739*8212SMichael.Corcoran@Sun.COM * 740*8212SMichael.Corcoran@Sun.COM * Returns the starting virtual address for the range to be mapped or NULL 741*8212SMichael.Corcoran@Sun.COM * if an error is encountered. If we successfully insert the requested info 742*8212SMichael.Corcoran@Sun.COM * into the lib_va hash, then *lvpp will be set to point to this lib_va 743*8212SMichael.Corcoran@Sun.COM * structure. The structure will have a hold on it and thus lib_va_release 744*8212SMichael.Corcoran@Sun.COM * needs to be called on it by the caller. This function will not fill out 745*8212SMichael.Corcoran@Sun.COM * lv_mps or lv_num_segs since it does not have enough information to do so. 746*8212SMichael.Corcoran@Sun.COM * The caller is responsible for doing this making sure that any modifications 747*8212SMichael.Corcoran@Sun.COM * to lv_mps are visible before setting lv_num_segs. 748*8212SMichael.Corcoran@Sun.COM */ 749*8212SMichael.Corcoran@Sun.COM static caddr_t 750*8212SMichael.Corcoran@Sun.COM mmapobj_alloc_start_addr(struct lib_va **lvpp, size_t len, int use_lib_va, 751*8212SMichael.Corcoran@Sun.COM size_t align, vattr_t *vap) 752*8212SMichael.Corcoran@Sun.COM { 753*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 754*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 755*8212SMichael.Corcoran@Sun.COM int error; 756*8212SMichael.Corcoran@Sun.COM model_t model; 757*8212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 758*8212SMichael.Corcoran@Sun.COM caddr_t base = NULL; 759*8212SMichael.Corcoran@Sun.COM vmem_t *model_vmem; 760*8212SMichael.Corcoran@Sun.COM 761*8212SMichael.Corcoran@Sun.COM ASSERT(lvpp != NULL); 762*8212SMichael.Corcoran@Sun.COM 763*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_start); 764*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 765*8212SMichael.Corcoran@Sun.COM 766*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 767*8212SMichael.Corcoran@Sun.COM ma_flags = 0; 768*8212SMichael.Corcoran@Sun.COM model_vmem = lib_va_64_arena; 769*8212SMichael.Corcoran@Sun.COM } else { 770*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 771*8212SMichael.Corcoran@Sun.COM model_vmem = lib_va_32_arena; 772*8212SMichael.Corcoran@Sun.COM } 773*8212SMichael.Corcoran@Sun.COM 774*8212SMichael.Corcoran@Sun.COM if (align > 1) { 775*8212SMichael.Corcoran@Sun.COM ma_flags |= MAP_ALIGN; 776*8212SMichael.Corcoran@Sun.COM } 777*8212SMichael.Corcoran@Sun.COM if (use_lib_va) { 778*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64 || libs_mapped_32 < lib_threshold) { 779*8212SMichael.Corcoran@Sun.COM base = vmem_xalloc(model_vmem, len, align, 0, 0, NULL, 780*8212SMichael.Corcoran@Sun.COM NULL, VM_NOSLEEP | VM_ENDALLOC); 781*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_vmem); 782*8212SMichael.Corcoran@Sun.COM } 783*8212SMichael.Corcoran@Sun.COM #ifdef DEBUG 784*8212SMichael.Corcoran@Sun.COM /* 785*8212SMichael.Corcoran@Sun.COM * Check to see if we've run into ld.so.1. 786*8212SMichael.Corcoran@Sun.COM * If this is the first library we've mapped and we can not 787*8212SMichael.Corcoran@Sun.COM * use our reserved address space, then it's likely that 788*8212SMichael.Corcoran@Sun.COM * ld.so.1 is occupying some of this space and the 789*8212SMichael.Corcoran@Sun.COM * model_vmem arena bounds need to be changed. If we've run 790*8212SMichael.Corcoran@Sun.COM * into something else besides ld.so.1 we'll see this message 791*8212SMichael.Corcoran@Sun.COM * on the first use of mmapobj and should ignore the message. 792*8212SMichael.Corcoran@Sun.COM */ 793*8212SMichael.Corcoran@Sun.COM if (base != NULL && libs_mapped_32 == 0 && 794*8212SMichael.Corcoran@Sun.COM model == DATAMODEL_ILP32 && 795*8212SMichael.Corcoran@Sun.COM as_gap(as, len, &base, &len, 0, NULL)) { 796*8212SMichael.Corcoran@Sun.COM cmn_err(CE_NOTE, 797*8212SMichael.Corcoran@Sun.COM "lib_va_32_arena may not be optimized"); 798*8212SMichael.Corcoran@Sun.COM } else if (base != NULL && libs_mapped_64 == 0 && 799*8212SMichael.Corcoran@Sun.COM model == DATAMODEL_LP64 && 800*8212SMichael.Corcoran@Sun.COM as_gap(as, len, &base, &len, 0, NULL)) { 801*8212SMichael.Corcoran@Sun.COM cmn_err(CE_NOTE, 802*8212SMichael.Corcoran@Sun.COM "lib_va_64_arena may not be optimized"); 803*8212SMichael.Corcoran@Sun.COM } 804*8212SMichael.Corcoran@Sun.COM #endif 805*8212SMichael.Corcoran@Sun.COM /* 806*8212SMichael.Corcoran@Sun.COM * Even if the address fails to fit in our address space, 807*8212SMichael.Corcoran@Sun.COM * or we can't use a reserved address, 808*8212SMichael.Corcoran@Sun.COM * we should still save it off in lib_va_hash. 809*8212SMichael.Corcoran@Sun.COM */ 810*8212SMichael.Corcoran@Sun.COM *lvpp = lib_va_add_hash(base, len, align, vap); 811*8212SMichael.Corcoran@Sun.COM 812*8212SMichael.Corcoran@Sun.COM /* 813*8212SMichael.Corcoran@Sun.COM * Check for collision on insertion and free up our VA space. 814*8212SMichael.Corcoran@Sun.COM * This is expected to be rare, so we'll just reset base to 815*8212SMichael.Corcoran@Sun.COM * NULL instead of looking it up in the lib_va hash. 816*8212SMichael.Corcoran@Sun.COM */ 817*8212SMichael.Corcoran@Sun.COM if (*lvpp == NULL) { 818*8212SMichael.Corcoran@Sun.COM if (base != NULL) { 819*8212SMichael.Corcoran@Sun.COM vmem_xfree(model_vmem, base, len); 820*8212SMichael.Corcoran@Sun.COM base = NULL; 821*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(add_collision); 822*8212SMichael.Corcoran@Sun.COM } 823*8212SMichael.Corcoran@Sun.COM } 824*8212SMichael.Corcoran@Sun.COM } 825*8212SMichael.Corcoran@Sun.COM 826*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 827*8212SMichael.Corcoran@Sun.COM 828*8212SMichael.Corcoran@Sun.COM /* 829*8212SMichael.Corcoran@Sun.COM * If we don't have an expected base address, or the one that we want 830*8212SMichael.Corcoran@Sun.COM * to use is not available, go get an acceptable address range. 831*8212SMichael.Corcoran@Sun.COM */ 832*8212SMichael.Corcoran@Sun.COM if (base == NULL || as_gap(as, len, &base, &len, 0, NULL)) { 833*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(get_addr); 834*8212SMichael.Corcoran@Sun.COM base = (caddr_t)align; 835*8212SMichael.Corcoran@Sun.COM map_addr(&base, len, 0, 1, ma_flags); 836*8212SMichael.Corcoran@Sun.COM } 837*8212SMichael.Corcoran@Sun.COM 838*8212SMichael.Corcoran@Sun.COM /* 839*8212SMichael.Corcoran@Sun.COM * Need to reserve the address space we're going to use. 840*8212SMichael.Corcoran@Sun.COM * Don't reserve swap space since we'll be mapping over this. 841*8212SMichael.Corcoran@Sun.COM */ 842*8212SMichael.Corcoran@Sun.COM if (base != NULL) { 843*8212SMichael.Corcoran@Sun.COM /* Don't reserve swap space since we'll be mapping over this */ 844*8212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 845*8212SMichael.Corcoran@Sun.COM error = as_map(as, base, len, segvn_create, &crargs); 846*8212SMichael.Corcoran@Sun.COM if (error) { 847*8212SMichael.Corcoran@Sun.COM base = NULL; 848*8212SMichael.Corcoran@Sun.COM } 849*8212SMichael.Corcoran@Sun.COM } 850*8212SMichael.Corcoran@Sun.COM 851*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 852*8212SMichael.Corcoran@Sun.COM return (base); 853*8212SMichael.Corcoran@Sun.COM } 854*8212SMichael.Corcoran@Sun.COM 855*8212SMichael.Corcoran@Sun.COM /* 856*8212SMichael.Corcoran@Sun.COM * Map the file associated with vp into the address space as a single 857*8212SMichael.Corcoran@Sun.COM * read only private mapping. 858*8212SMichael.Corcoran@Sun.COM * Returns 0 for success, and non-zero for failure to map the file. 859*8212SMichael.Corcoran@Sun.COM */ 860*8212SMichael.Corcoran@Sun.COM static int 861*8212SMichael.Corcoran@Sun.COM mmapobj_map_flat(vnode_t *vp, mmapobj_result_t *mrp, size_t padding, 862*8212SMichael.Corcoran@Sun.COM cred_t *fcred) 863*8212SMichael.Corcoran@Sun.COM { 864*8212SMichael.Corcoran@Sun.COM int error = 0; 865*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 866*8212SMichael.Corcoran@Sun.COM caddr_t addr = NULL; 867*8212SMichael.Corcoran@Sun.COM caddr_t start_addr; 868*8212SMichael.Corcoran@Sun.COM size_t len; 869*8212SMichael.Corcoran@Sun.COM size_t pad_len; 870*8212SMichael.Corcoran@Sun.COM int prot = PROT_USER | PROT_READ; 871*8212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 872*8212SMichael.Corcoran@Sun.COM vattr_t vattr; 873*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 874*8212SMichael.Corcoran@Sun.COM 875*8212SMichael.Corcoran@Sun.COM if (get_udatamodel() == DATAMODEL_LP64) { 876*8212SMichael.Corcoran@Sun.COM ma_flags = 0; 877*8212SMichael.Corcoran@Sun.COM } 878*8212SMichael.Corcoran@Sun.COM 879*8212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_SIZE; 880*8212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 881*8212SMichael.Corcoran@Sun.COM if (error) { 882*8212SMichael.Corcoran@Sun.COM return (error); 883*8212SMichael.Corcoran@Sun.COM } 884*8212SMichael.Corcoran@Sun.COM 885*8212SMichael.Corcoran@Sun.COM len = vattr.va_size; 886*8212SMichael.Corcoran@Sun.COM 887*8212SMichael.Corcoran@Sun.COM ma_flags |= MAP_PRIVATE; 888*8212SMichael.Corcoran@Sun.COM if (padding == 0) { 889*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_flat_no_padding); 890*8212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, 0, as, &addr, len, prot, PROT_ALL, 891*8212SMichael.Corcoran@Sun.COM ma_flags, fcred, NULL); 892*8212SMichael.Corcoran@Sun.COM if (error == 0) { 893*8212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = addr; 894*8212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = len; 895*8212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = len; 896*8212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 897*8212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = prot; 898*8212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = 0; 899*8212SMichael.Corcoran@Sun.COM } 900*8212SMichael.Corcoran@Sun.COM return (error); 901*8212SMichael.Corcoran@Sun.COM } 902*8212SMichael.Corcoran@Sun.COM 903*8212SMichael.Corcoran@Sun.COM /* padding was requested so there's more work to be done */ 904*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_flat_padding); 905*8212SMichael.Corcoran@Sun.COM 906*8212SMichael.Corcoran@Sun.COM /* No need to reserve swap space now since it will be reserved later */ 907*8212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 908*8212SMichael.Corcoran@Sun.COM 909*8212SMichael.Corcoran@Sun.COM /* Need to setup padding which can only be in PAGESIZE increments. */ 910*8212SMichael.Corcoran@Sun.COM ASSERT((padding & PAGEOFFSET) == 0); 911*8212SMichael.Corcoran@Sun.COM pad_len = len + (2 * padding); 912*8212SMichael.Corcoran@Sun.COM 913*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 914*8212SMichael.Corcoran@Sun.COM map_addr(&addr, pad_len, 0, 1, ma_flags); 915*8212SMichael.Corcoran@Sun.COM error = as_map(as, addr, pad_len, segvn_create, &crargs); 916*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 917*8212SMichael.Corcoran@Sun.COM if (error) { 918*8212SMichael.Corcoran@Sun.COM return (error); 919*8212SMichael.Corcoran@Sun.COM } 920*8212SMichael.Corcoran@Sun.COM start_addr = addr; 921*8212SMichael.Corcoran@Sun.COM addr += padding; 922*8212SMichael.Corcoran@Sun.COM ma_flags |= MAP_FIXED; 923*8212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, 0, as, &addr, len, prot, PROT_ALL, ma_flags, 924*8212SMichael.Corcoran@Sun.COM fcred, NULL); 925*8212SMichael.Corcoran@Sun.COM if (error == 0) { 926*8212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = start_addr; 927*8212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = padding; 928*8212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = 0; 929*8212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 930*8212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = 0; 931*8212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_PADDING; 932*8212SMichael.Corcoran@Sun.COM 933*8212SMichael.Corcoran@Sun.COM mrp[1].mr_addr = addr; 934*8212SMichael.Corcoran@Sun.COM mrp[1].mr_msize = len; 935*8212SMichael.Corcoran@Sun.COM mrp[1].mr_fsize = len; 936*8212SMichael.Corcoran@Sun.COM mrp[1].mr_offset = 0; 937*8212SMichael.Corcoran@Sun.COM mrp[1].mr_prot = prot; 938*8212SMichael.Corcoran@Sun.COM mrp[1].mr_flags = 0; 939*8212SMichael.Corcoran@Sun.COM 940*8212SMichael.Corcoran@Sun.COM mrp[2].mr_addr = addr + P2ROUNDUP(len, PAGESIZE); 941*8212SMichael.Corcoran@Sun.COM mrp[2].mr_msize = padding; 942*8212SMichael.Corcoran@Sun.COM mrp[2].mr_fsize = 0; 943*8212SMichael.Corcoran@Sun.COM mrp[2].mr_offset = 0; 944*8212SMichael.Corcoran@Sun.COM mrp[2].mr_prot = 0; 945*8212SMichael.Corcoran@Sun.COM mrp[2].mr_flags = MR_PADDING; 946*8212SMichael.Corcoran@Sun.COM } else { 947*8212SMichael.Corcoran@Sun.COM /* Need to cleanup the as_map from earlier */ 948*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, pad_len); 949*8212SMichael.Corcoran@Sun.COM } 950*8212SMichael.Corcoran@Sun.COM return (error); 951*8212SMichael.Corcoran@Sun.COM } 952*8212SMichael.Corcoran@Sun.COM 953*8212SMichael.Corcoran@Sun.COM /* 954*8212SMichael.Corcoran@Sun.COM * Map a PT_LOAD or PT_SUNWBSS section of an executable file into the user's 955*8212SMichael.Corcoran@Sun.COM * address space. 956*8212SMichael.Corcoran@Sun.COM * vp - vnode to be mapped in 957*8212SMichael.Corcoran@Sun.COM * addr - start address 958*8212SMichael.Corcoran@Sun.COM * len - length of vp to be mapped 959*8212SMichael.Corcoran@Sun.COM * zfodlen - length of zero filled memory after len above 960*8212SMichael.Corcoran@Sun.COM * offset - offset into file where mapping should start 961*8212SMichael.Corcoran@Sun.COM * prot - protections for this mapping 962*8212SMichael.Corcoran@Sun.COM * fcred - credentials for the file associated with vp at open time. 963*8212SMichael.Corcoran@Sun.COM */ 964*8212SMichael.Corcoran@Sun.COM static int 965*8212SMichael.Corcoran@Sun.COM mmapobj_map_ptload(struct vnode *vp, caddr_t addr, size_t len, size_t zfodlen, 966*8212SMichael.Corcoran@Sun.COM off_t offset, int prot, cred_t *fcred) 967*8212SMichael.Corcoran@Sun.COM { 968*8212SMichael.Corcoran@Sun.COM int error = 0; 969*8212SMichael.Corcoran@Sun.COM caddr_t zfodbase, oldaddr; 970*8212SMichael.Corcoran@Sun.COM size_t oldlen; 971*8212SMichael.Corcoran@Sun.COM size_t end; 972*8212SMichael.Corcoran@Sun.COM size_t zfoddiff; 973*8212SMichael.Corcoran@Sun.COM label_t ljb; 974*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 975*8212SMichael.Corcoran@Sun.COM model_t model; 976*8212SMichael.Corcoran@Sun.COM int full_page; 977*8212SMichael.Corcoran@Sun.COM 978*8212SMichael.Corcoran@Sun.COM /* 979*8212SMichael.Corcoran@Sun.COM * See if addr and offset are aligned such that we can map in 980*8212SMichael.Corcoran@Sun.COM * full pages instead of partial pages. 981*8212SMichael.Corcoran@Sun.COM */ 982*8212SMichael.Corcoran@Sun.COM full_page = (((uintptr_t)addr & PAGEOFFSET) == 983*8212SMichael.Corcoran@Sun.COM ((uintptr_t)offset & PAGEOFFSET)); 984*8212SMichael.Corcoran@Sun.COM 985*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 986*8212SMichael.Corcoran@Sun.COM 987*8212SMichael.Corcoran@Sun.COM oldaddr = addr; 988*8212SMichael.Corcoran@Sun.COM addr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 989*8212SMichael.Corcoran@Sun.COM if (len) { 990*8212SMichael.Corcoran@Sun.COM spgcnt_t availm, npages; 991*8212SMichael.Corcoran@Sun.COM int preread; 992*8212SMichael.Corcoran@Sun.COM uint_t mflag = MAP_PRIVATE | MAP_FIXED; 993*8212SMichael.Corcoran@Sun.COM 994*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_ILP32) { 995*8212SMichael.Corcoran@Sun.COM mflag |= _MAP_LOW32; 996*8212SMichael.Corcoran@Sun.COM } 997*8212SMichael.Corcoran@Sun.COM /* We may need to map in extra bytes */ 998*8212SMichael.Corcoran@Sun.COM oldlen = len; 999*8212SMichael.Corcoran@Sun.COM len += ((size_t)oldaddr & PAGEOFFSET); 1000*8212SMichael.Corcoran@Sun.COM 1001*8212SMichael.Corcoran@Sun.COM if (full_page) { 1002*8212SMichael.Corcoran@Sun.COM offset = (off_t)((uintptr_t)offset & PAGEMASK); 1003*8212SMichael.Corcoran@Sun.COM if ((prot & (PROT_WRITE | PROT_EXEC)) == PROT_EXEC) { 1004*8212SMichael.Corcoran@Sun.COM mflag |= MAP_TEXT; 1005*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_text); 1006*8212SMichael.Corcoran@Sun.COM } else { 1007*8212SMichael.Corcoran@Sun.COM mflag |= MAP_INITDATA; 1008*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_initdata); 1009*8212SMichael.Corcoran@Sun.COM } 1010*8212SMichael.Corcoran@Sun.COM 1011*8212SMichael.Corcoran@Sun.COM /* 1012*8212SMichael.Corcoran@Sun.COM * maxprot is passed as PROT_ALL so that mdb can 1013*8212SMichael.Corcoran@Sun.COM * write to this segment. 1014*8212SMichael.Corcoran@Sun.COM */ 1015*8212SMichael.Corcoran@Sun.COM if (error = VOP_MAP(vp, (offset_t)offset, as, &addr, 1016*8212SMichael.Corcoran@Sun.COM len, prot, PROT_ALL, mflag, fcred, NULL)) { 1017*8212SMichael.Corcoran@Sun.COM return (error); 1018*8212SMichael.Corcoran@Sun.COM } 1019*8212SMichael.Corcoran@Sun.COM 1020*8212SMichael.Corcoran@Sun.COM /* 1021*8212SMichael.Corcoran@Sun.COM * If the segment can fit and is relatively small, then 1022*8212SMichael.Corcoran@Sun.COM * we prefault the entire segment in. This is based 1023*8212SMichael.Corcoran@Sun.COM * on the model that says the best working set of a 1024*8212SMichael.Corcoran@Sun.COM * small program is all of its pages. 1025*8212SMichael.Corcoran@Sun.COM * We only do this if freemem will not drop below 1026*8212SMichael.Corcoran@Sun.COM * lotsfree since we don't want to induce paging. 1027*8212SMichael.Corcoran@Sun.COM */ 1028*8212SMichael.Corcoran@Sun.COM npages = (spgcnt_t)btopr(len); 1029*8212SMichael.Corcoran@Sun.COM availm = freemem - lotsfree; 1030*8212SMichael.Corcoran@Sun.COM preread = (npages < availm && len < PGTHRESH) ? 1 : 0; 1031*8212SMichael.Corcoran@Sun.COM 1032*8212SMichael.Corcoran@Sun.COM /* 1033*8212SMichael.Corcoran@Sun.COM * If we aren't prefaulting the segment, 1034*8212SMichael.Corcoran@Sun.COM * increment "deficit", if necessary to ensure 1035*8212SMichael.Corcoran@Sun.COM * that pages will become available when this 1036*8212SMichael.Corcoran@Sun.COM * process starts executing. 1037*8212SMichael.Corcoran@Sun.COM */ 1038*8212SMichael.Corcoran@Sun.COM if (preread == 0 && npages > availm && 1039*8212SMichael.Corcoran@Sun.COM deficit < lotsfree) { 1040*8212SMichael.Corcoran@Sun.COM deficit += MIN((pgcnt_t)(npages - availm), 1041*8212SMichael.Corcoran@Sun.COM lotsfree - deficit); 1042*8212SMichael.Corcoran@Sun.COM } 1043*8212SMichael.Corcoran@Sun.COM 1044*8212SMichael.Corcoran@Sun.COM if (preread) { 1045*8212SMichael.Corcoran@Sun.COM (void) as_faulta(as, addr, len); 1046*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_preread); 1047*8212SMichael.Corcoran@Sun.COM } 1048*8212SMichael.Corcoran@Sun.COM } else { 1049*8212SMichael.Corcoran@Sun.COM /* 1050*8212SMichael.Corcoran@Sun.COM * addr and offset were not aligned such that we could 1051*8212SMichael.Corcoran@Sun.COM * use VOP_MAP, thus we need to as_map the memory we 1052*8212SMichael.Corcoran@Sun.COM * need and then read the data in from disk. 1053*8212SMichael.Corcoran@Sun.COM * This code path is a corner case which should never 1054*8212SMichael.Corcoran@Sun.COM * be taken, but hand crafted binaries could trigger 1055*8212SMichael.Corcoran@Sun.COM * this logic and it needs to work correctly. 1056*8212SMichael.Corcoran@Sun.COM */ 1057*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_text); 1058*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 1059*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, len); 1060*8212SMichael.Corcoran@Sun.COM 1061*8212SMichael.Corcoran@Sun.COM /* 1062*8212SMichael.Corcoran@Sun.COM * We use zfod_argsp because we need to be able to 1063*8212SMichael.Corcoran@Sun.COM * write to the mapping and then we'll change the 1064*8212SMichael.Corcoran@Sun.COM * protections later if they are incorrect. 1065*8212SMichael.Corcoran@Sun.COM */ 1066*8212SMichael.Corcoran@Sun.COM error = as_map(as, addr, len, segvn_create, zfod_argsp); 1067*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1068*8212SMichael.Corcoran@Sun.COM if (error) { 1069*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_map_fail); 1070*8212SMichael.Corcoran@Sun.COM return (error); 1071*8212SMichael.Corcoran@Sun.COM } 1072*8212SMichael.Corcoran@Sun.COM 1073*8212SMichael.Corcoran@Sun.COM /* Now read in the data from disk */ 1074*8212SMichael.Corcoran@Sun.COM error = vn_rdwr(UIO_READ, vp, oldaddr, oldlen, offset, 1075*8212SMichael.Corcoran@Sun.COM UIO_USERSPACE, 0, (rlim64_t)0, fcred, NULL); 1076*8212SMichael.Corcoran@Sun.COM if (error) { 1077*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_read_fail); 1078*8212SMichael.Corcoran@Sun.COM return (error); 1079*8212SMichael.Corcoran@Sun.COM } 1080*8212SMichael.Corcoran@Sun.COM 1081*8212SMichael.Corcoran@Sun.COM /* 1082*8212SMichael.Corcoran@Sun.COM * Now set protections. 1083*8212SMichael.Corcoran@Sun.COM */ 1084*8212SMichael.Corcoran@Sun.COM if (prot != PROT_ZFOD) { 1085*8212SMichael.Corcoran@Sun.COM (void) as_setprot(as, addr, len, prot); 1086*8212SMichael.Corcoran@Sun.COM } 1087*8212SMichael.Corcoran@Sun.COM } 1088*8212SMichael.Corcoran@Sun.COM } 1089*8212SMichael.Corcoran@Sun.COM 1090*8212SMichael.Corcoran@Sun.COM if (zfodlen) { 1091*8212SMichael.Corcoran@Sun.COM end = (size_t)addr + len; 1092*8212SMichael.Corcoran@Sun.COM zfodbase = (caddr_t)P2ROUNDUP(end, PAGESIZE); 1093*8212SMichael.Corcoran@Sun.COM zfoddiff = (uintptr_t)zfodbase - end; 1094*8212SMichael.Corcoran@Sun.COM if (zfoddiff) { 1095*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfoddiff); 1096*8212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 1097*8212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 1098*8212SMichael.Corcoran@Sun.COM zfoddiff, prot | PROT_WRITE); 1099*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfoddiff_nowrite); 1100*8212SMichael.Corcoran@Sun.COM } 1101*8212SMichael.Corcoran@Sun.COM if (on_fault(&ljb)) { 1102*8212SMichael.Corcoran@Sun.COM no_fault(); 1103*8212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 1104*8212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 1105*8212SMichael.Corcoran@Sun.COM zfoddiff, prot); 1106*8212SMichael.Corcoran@Sun.COM } 1107*8212SMichael.Corcoran@Sun.COM return (EFAULT); 1108*8212SMichael.Corcoran@Sun.COM } 1109*8212SMichael.Corcoran@Sun.COM uzero((void *)end, zfoddiff); 1110*8212SMichael.Corcoran@Sun.COM no_fault(); 1111*8212SMichael.Corcoran@Sun.COM 1112*8212SMichael.Corcoran@Sun.COM /* 1113*8212SMichael.Corcoran@Sun.COM * Remove write protection to return to original state 1114*8212SMichael.Corcoran@Sun.COM */ 1115*8212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 1116*8212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 1117*8212SMichael.Corcoran@Sun.COM zfoddiff, prot); 1118*8212SMichael.Corcoran@Sun.COM } 1119*8212SMichael.Corcoran@Sun.COM } 1120*8212SMichael.Corcoran@Sun.COM if (zfodlen > zfoddiff) { 1121*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = 1122*8212SMichael.Corcoran@Sun.COM SEGVN_ZFOD_ARGS(prot, PROT_ALL); 1123*8212SMichael.Corcoran@Sun.COM 1124*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfodextra); 1125*8212SMichael.Corcoran@Sun.COM zfodlen -= zfoddiff; 1126*8212SMichael.Corcoran@Sun.COM crargs.szc = AS_MAP_NO_LPOOB; 1127*8212SMichael.Corcoran@Sun.COM 1128*8212SMichael.Corcoran@Sun.COM 1129*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 1130*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, (caddr_t)zfodbase, zfodlen); 1131*8212SMichael.Corcoran@Sun.COM error = as_map(as, (caddr_t)zfodbase, 1132*8212SMichael.Corcoran@Sun.COM zfodlen, segvn_create, &crargs); 1133*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1134*8212SMichael.Corcoran@Sun.COM if (error) { 1135*8212SMichael.Corcoran@Sun.COM return (error); 1136*8212SMichael.Corcoran@Sun.COM } 1137*8212SMichael.Corcoran@Sun.COM } 1138*8212SMichael.Corcoran@Sun.COM } 1139*8212SMichael.Corcoran@Sun.COM return (0); 1140*8212SMichael.Corcoran@Sun.COM } 1141*8212SMichael.Corcoran@Sun.COM 1142*8212SMichael.Corcoran@Sun.COM /* 1143*8212SMichael.Corcoran@Sun.COM * Map the ELF file represented by vp into the users address space. The 1144*8212SMichael.Corcoran@Sun.COM * first mapping will start at start_addr and there will be num_elements 1145*8212SMichael.Corcoran@Sun.COM * mappings. The mappings are described by the data in mrp which may be 1146*8212SMichael.Corcoran@Sun.COM * modified upon returning from this function. 1147*8212SMichael.Corcoran@Sun.COM * Returns 0 for success or errno for failure. 1148*8212SMichael.Corcoran@Sun.COM */ 1149*8212SMichael.Corcoran@Sun.COM static int 1150*8212SMichael.Corcoran@Sun.COM mmapobj_map_elf(struct vnode *vp, caddr_t start_addr, mmapobj_result_t *mrp, 1151*8212SMichael.Corcoran@Sun.COM int num_elements, cred_t *fcred, ushort_t e_type) 1152*8212SMichael.Corcoran@Sun.COM { 1153*8212SMichael.Corcoran@Sun.COM int i; 1154*8212SMichael.Corcoran@Sun.COM int ret; 1155*8212SMichael.Corcoran@Sun.COM caddr_t lo; 1156*8212SMichael.Corcoran@Sun.COM caddr_t hi; 1157*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 1158*8212SMichael.Corcoran@Sun.COM 1159*8212SMichael.Corcoran@Sun.COM for (i = 0; i < num_elements; i++) { 1160*8212SMichael.Corcoran@Sun.COM caddr_t addr; 1161*8212SMichael.Corcoran@Sun.COM size_t p_memsz; 1162*8212SMichael.Corcoran@Sun.COM size_t p_filesz; 1163*8212SMichael.Corcoran@Sun.COM size_t zfodlen; 1164*8212SMichael.Corcoran@Sun.COM offset_t p_offset; 1165*8212SMichael.Corcoran@Sun.COM size_t dif; 1166*8212SMichael.Corcoran@Sun.COM int prot; 1167*8212SMichael.Corcoran@Sun.COM 1168*8212SMichael.Corcoran@Sun.COM /* Always need to adjust mr_addr */ 1169*8212SMichael.Corcoran@Sun.COM addr = start_addr + (size_t)(mrp[i].mr_addr); 1170*8212SMichael.Corcoran@Sun.COM mrp[i].mr_addr = 1171*8212SMichael.Corcoran@Sun.COM (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 1172*8212SMichael.Corcoran@Sun.COM 1173*8212SMichael.Corcoran@Sun.COM /* Padding has already been mapped */ 1174*8212SMichael.Corcoran@Sun.COM if (MR_GET_TYPE(mrp[i].mr_flags) == MR_PADDING) { 1175*8212SMichael.Corcoran@Sun.COM continue; 1176*8212SMichael.Corcoran@Sun.COM } 1177*8212SMichael.Corcoran@Sun.COM p_memsz = mrp[i].mr_msize; 1178*8212SMichael.Corcoran@Sun.COM p_filesz = mrp[i].mr_fsize; 1179*8212SMichael.Corcoran@Sun.COM zfodlen = p_memsz - p_filesz; 1180*8212SMichael.Corcoran@Sun.COM p_offset = mrp[i].mr_offset; 1181*8212SMichael.Corcoran@Sun.COM dif = (uintptr_t)(addr) & PAGEOFFSET; 1182*8212SMichael.Corcoran@Sun.COM prot = mrp[i].mr_prot | PROT_USER; 1183*8212SMichael.Corcoran@Sun.COM ret = mmapobj_map_ptload(vp, addr, p_filesz, zfodlen, 1184*8212SMichael.Corcoran@Sun.COM p_offset, prot, fcred); 1185*8212SMichael.Corcoran@Sun.COM if (ret != 0) { 1186*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(ptload_failed); 1187*8212SMichael.Corcoran@Sun.COM mmapobj_unmap(mrp, i, num_elements, e_type); 1188*8212SMichael.Corcoran@Sun.COM return (ret); 1189*8212SMichael.Corcoran@Sun.COM } 1190*8212SMichael.Corcoran@Sun.COM 1191*8212SMichael.Corcoran@Sun.COM /* Need to cleanup mrp to reflect the actual values used */ 1192*8212SMichael.Corcoran@Sun.COM mrp[i].mr_msize += dif; 1193*8212SMichael.Corcoran@Sun.COM mrp[i].mr_offset = (size_t)addr & PAGEOFFSET; 1194*8212SMichael.Corcoran@Sun.COM } 1195*8212SMichael.Corcoran@Sun.COM 1196*8212SMichael.Corcoran@Sun.COM /* Also need to unmap any holes created above */ 1197*8212SMichael.Corcoran@Sun.COM if (num_elements == 1) { 1198*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_elf_no_holes); 1199*8212SMichael.Corcoran@Sun.COM return (0); 1200*8212SMichael.Corcoran@Sun.COM } 1201*8212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 1202*8212SMichael.Corcoran@Sun.COM return (0); 1203*8212SMichael.Corcoran@Sun.COM } 1204*8212SMichael.Corcoran@Sun.COM 1205*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 1206*8212SMichael.Corcoran@Sun.COM lo = start_addr; 1207*8212SMichael.Corcoran@Sun.COM hi = mrp[0].mr_addr; 1208*8212SMichael.Corcoran@Sun.COM 1209*8212SMichael.Corcoran@Sun.COM /* Remove holes made by the rest of the segments */ 1210*8212SMichael.Corcoran@Sun.COM for (i = 0; i < num_elements - 1; i++) { 1211*8212SMichael.Corcoran@Sun.COM lo = (caddr_t)P2ROUNDUP((size_t)(mrp[i].mr_addr) + 1212*8212SMichael.Corcoran@Sun.COM mrp[i].mr_msize, PAGESIZE); 1213*8212SMichael.Corcoran@Sun.COM hi = mrp[i + 1].mr_addr; 1214*8212SMichael.Corcoran@Sun.COM if (lo < hi) { 1215*8212SMichael.Corcoran@Sun.COM /* 1216*8212SMichael.Corcoran@Sun.COM * If as_unmap fails we just use up a bit of extra 1217*8212SMichael.Corcoran@Sun.COM * space 1218*8212SMichael.Corcoran@Sun.COM */ 1219*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, (caddr_t)lo, 1220*8212SMichael.Corcoran@Sun.COM (size_t)hi - (size_t)lo); 1221*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unmap_hole); 1222*8212SMichael.Corcoran@Sun.COM } 1223*8212SMichael.Corcoran@Sun.COM } 1224*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1225*8212SMichael.Corcoran@Sun.COM 1226*8212SMichael.Corcoran@Sun.COM return (0); 1227*8212SMichael.Corcoran@Sun.COM } 1228*8212SMichael.Corcoran@Sun.COM 1229*8212SMichael.Corcoran@Sun.COM /* Ugly hack to get STRUCT_* macros to work below */ 1230*8212SMichael.Corcoran@Sun.COM struct myphdr { 1231*8212SMichael.Corcoran@Sun.COM Phdr x; /* native version */ 1232*8212SMichael.Corcoran@Sun.COM }; 1233*8212SMichael.Corcoran@Sun.COM 1234*8212SMichael.Corcoran@Sun.COM struct myphdr32 { 1235*8212SMichael.Corcoran@Sun.COM Elf32_Phdr x; 1236*8212SMichael.Corcoran@Sun.COM }; 1237*8212SMichael.Corcoran@Sun.COM 1238*8212SMichael.Corcoran@Sun.COM /* 1239*8212SMichael.Corcoran@Sun.COM * Calculate and return the number of loadable segments in the ELF Phdr 1240*8212SMichael.Corcoran@Sun.COM * represented by phdrbase as well as the len of the total mapping and 1241*8212SMichael.Corcoran@Sun.COM * the max alignment that is needed for a given segment. On success, 1242*8212SMichael.Corcoran@Sun.COM * 0 is returned, and *len, *loadable and *align have been filled out. 1243*8212SMichael.Corcoran@Sun.COM * On failure, errno will be returned, which in this case is ENOTSUP 1244*8212SMichael.Corcoran@Sun.COM * if we were passed an ELF file with overlapping segments. 1245*8212SMichael.Corcoran@Sun.COM */ 1246*8212SMichael.Corcoran@Sun.COM static int 1247*8212SMichael.Corcoran@Sun.COM calc_loadable(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, size_t *len, 1248*8212SMichael.Corcoran@Sun.COM int *loadable, size_t *align) 1249*8212SMichael.Corcoran@Sun.COM { 1250*8212SMichael.Corcoran@Sun.COM int i; 1251*8212SMichael.Corcoran@Sun.COM int hsize; 1252*8212SMichael.Corcoran@Sun.COM model_t model; 1253*8212SMichael.Corcoran@Sun.COM uint_t p_type; 1254*8212SMichael.Corcoran@Sun.COM offset_t p_offset; 1255*8212SMichael.Corcoran@Sun.COM size_t p_memsz; 1256*8212SMichael.Corcoran@Sun.COM size_t p_align; 1257*8212SMichael.Corcoran@Sun.COM caddr_t vaddr; 1258*8212SMichael.Corcoran@Sun.COM int num_segs = 0; 1259*8212SMichael.Corcoran@Sun.COM caddr_t start_addr = NULL; 1260*8212SMichael.Corcoran@Sun.COM caddr_t p_end = NULL; 1261*8212SMichael.Corcoran@Sun.COM size_t max_align = 0; 1262*8212SMichael.Corcoran@Sun.COM STRUCT_HANDLE(myphdr, mph); 1263*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 1264*8212SMichael.Corcoran@Sun.COM extern int vac_size; 1265*8212SMichael.Corcoran@Sun.COM #endif 1266*8212SMichael.Corcoran@Sun.COM 1267*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 1268*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 1269*8212SMichael.Corcoran@Sun.COM 1270*8212SMichael.Corcoran@Sun.COM /* hsize alignment should have been checked before calling this func */ 1271*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 1272*8212SMichael.Corcoran@Sun.COM hsize = ehdrp->e_phentsize; 1273*8212SMichael.Corcoran@Sun.COM if (hsize & 7) { 1274*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1275*8212SMichael.Corcoran@Sun.COM } 1276*8212SMichael.Corcoran@Sun.COM } else { 1277*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 1278*8212SMichael.Corcoran@Sun.COM hsize = ((Elf32_Ehdr *)ehdrp)->e_phentsize; 1279*8212SMichael.Corcoran@Sun.COM if (hsize & 3) { 1280*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1281*8212SMichael.Corcoran@Sun.COM } 1282*8212SMichael.Corcoran@Sun.COM } 1283*8212SMichael.Corcoran@Sun.COM 1284*8212SMichael.Corcoran@Sun.COM /* 1285*8212SMichael.Corcoran@Sun.COM * Determine the span of all loadable segments and calculate the 1286*8212SMichael.Corcoran@Sun.COM * number of loadable segments. 1287*8212SMichael.Corcoran@Sun.COM */ 1288*8212SMichael.Corcoran@Sun.COM for (i = 0; i < nphdrs; i++) { 1289*8212SMichael.Corcoran@Sun.COM p_type = STRUCT_FGET(mph, x.p_type); 1290*8212SMichael.Corcoran@Sun.COM if (p_type == PT_LOAD || p_type == PT_SUNWBSS) { 1291*8212SMichael.Corcoran@Sun.COM vaddr = (caddr_t)(uintptr_t)STRUCT_FGET(mph, x.p_vaddr); 1292*8212SMichael.Corcoran@Sun.COM p_memsz = STRUCT_FGET(mph, x.p_memsz); 1293*8212SMichael.Corcoran@Sun.COM 1294*8212SMichael.Corcoran@Sun.COM /* 1295*8212SMichael.Corcoran@Sun.COM * Skip this header if it requests no memory to be 1296*8212SMichael.Corcoran@Sun.COM * mapped. 1297*8212SMichael.Corcoran@Sun.COM */ 1298*8212SMichael.Corcoran@Sun.COM if (p_memsz == 0) { 1299*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 1300*8212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 1301*8212SMichael.Corcoran@Sun.COM hsize)); 1302*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(nomem_header); 1303*8212SMichael.Corcoran@Sun.COM continue; 1304*8212SMichael.Corcoran@Sun.COM } 1305*8212SMichael.Corcoran@Sun.COM if (num_segs++ == 0) { 1306*8212SMichael.Corcoran@Sun.COM start_addr = vaddr; 1307*8212SMichael.Corcoran@Sun.COM /* 1308*8212SMichael.Corcoran@Sun.COM * For the first segment, we need to map from 1309*8212SMichael.Corcoran@Sun.COM * the beginning of the file, so we will 1310*8212SMichael.Corcoran@Sun.COM * adjust the size of the mapping to include 1311*8212SMichael.Corcoran@Sun.COM * this memory. 1312*8212SMichael.Corcoran@Sun.COM */ 1313*8212SMichael.Corcoran@Sun.COM p_offset = STRUCT_FGET(mph, x.p_offset); 1314*8212SMichael.Corcoran@Sun.COM } else { 1315*8212SMichael.Corcoran@Sun.COM p_offset = 0; 1316*8212SMichael.Corcoran@Sun.COM } 1317*8212SMichael.Corcoran@Sun.COM /* 1318*8212SMichael.Corcoran@Sun.COM * Check to make sure that this mapping wouldn't 1319*8212SMichael.Corcoran@Sun.COM * overlap a previous mapping. 1320*8212SMichael.Corcoran@Sun.COM */ 1321*8212SMichael.Corcoran@Sun.COM if (vaddr < p_end) { 1322*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(overlap_header); 1323*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1324*8212SMichael.Corcoran@Sun.COM } 1325*8212SMichael.Corcoran@Sun.COM 1326*8212SMichael.Corcoran@Sun.COM p_end = vaddr + p_memsz + p_offset; 1327*8212SMichael.Corcoran@Sun.COM p_end = (caddr_t)P2ROUNDUP((size_t)p_end, PAGESIZE); 1328*8212SMichael.Corcoran@Sun.COM 1329*8212SMichael.Corcoran@Sun.COM p_align = STRUCT_FGET(mph, x.p_align); 1330*8212SMichael.Corcoran@Sun.COM if (p_align > 1 && p_align > max_align) { 1331*8212SMichael.Corcoran@Sun.COM max_align = p_align; 1332*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 1333*8212SMichael.Corcoran@Sun.COM /* 1334*8212SMichael.Corcoran@Sun.COM * Want to prevent aliasing by making the start 1335*8212SMichael.Corcoran@Sun.COM * address be aligned to vac_size. 1336*8212SMichael.Corcoran@Sun.COM */ 1337*8212SMichael.Corcoran@Sun.COM if (max_align < vac_size) { 1338*8212SMichael.Corcoran@Sun.COM max_align = vac_size; 1339*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(vac_align); 1340*8212SMichael.Corcoran@Sun.COM } 1341*8212SMichael.Corcoran@Sun.COM #endif 1342*8212SMichael.Corcoran@Sun.COM } 1343*8212SMichael.Corcoran@Sun.COM } 1344*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 1345*8212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + hsize)); 1346*8212SMichael.Corcoran@Sun.COM } 1347*8212SMichael.Corcoran@Sun.COM 1348*8212SMichael.Corcoran@Sun.COM /* 1349*8212SMichael.Corcoran@Sun.COM * The alignment should be a power of 2, if it isn't we forgive it 1350*8212SMichael.Corcoran@Sun.COM * and round up. On overflow, we'll set the alignment to max_align 1351*8212SMichael.Corcoran@Sun.COM * rounded down to the nearest power of 2. 1352*8212SMichael.Corcoran@Sun.COM */ 1353*8212SMichael.Corcoran@Sun.COM if (max_align > 0 && !ISP2(max_align)) { 1354*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(np2_align); 1355*8212SMichael.Corcoran@Sun.COM *align = 2 * (1L << (highbit(max_align) - 1)); 1356*8212SMichael.Corcoran@Sun.COM if (*align < max_align || 1357*8212SMichael.Corcoran@Sun.COM (*align > UINT_MAX && model == DATAMODEL_ILP32)) { 1358*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(np2_align_overflow); 1359*8212SMichael.Corcoran@Sun.COM *align = 1L << (highbit(max_align) - 1); 1360*8212SMichael.Corcoran@Sun.COM } 1361*8212SMichael.Corcoran@Sun.COM } else { 1362*8212SMichael.Corcoran@Sun.COM *align = max_align; 1363*8212SMichael.Corcoran@Sun.COM } 1364*8212SMichael.Corcoran@Sun.COM 1365*8212SMichael.Corcoran@Sun.COM *loadable = num_segs; 1366*8212SMichael.Corcoran@Sun.COM *len = p_end - start_addr; 1367*8212SMichael.Corcoran@Sun.COM return (0); 1368*8212SMichael.Corcoran@Sun.COM } 1369*8212SMichael.Corcoran@Sun.COM 1370*8212SMichael.Corcoran@Sun.COM /* 1371*8212SMichael.Corcoran@Sun.COM * Check the address space to see if the virtual addresses to be used are 1372*8212SMichael.Corcoran@Sun.COM * available. If they are not, return errno for failure. On success, 0 1373*8212SMichael.Corcoran@Sun.COM * will be returned, and the virtual addresses for each mmapobj_result_t 1374*8212SMichael.Corcoran@Sun.COM * will be reserved. Note that a reservation could have earlier been made 1375*8212SMichael.Corcoran@Sun.COM * for a given segment via a /dev/null mapping. If that is the case, then 1376*8212SMichael.Corcoran@Sun.COM * we can use that VA space for our mappings. 1377*8212SMichael.Corcoran@Sun.COM * Note: this function will only be used for ET_EXEC binaries. 1378*8212SMichael.Corcoran@Sun.COM */ 1379*8212SMichael.Corcoran@Sun.COM int 1380*8212SMichael.Corcoran@Sun.COM check_exec_addrs(int loadable, mmapobj_result_t *mrp, caddr_t start_addr) 1381*8212SMichael.Corcoran@Sun.COM { 1382*8212SMichael.Corcoran@Sun.COM int i; 1383*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 1384*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 1385*8212SMichael.Corcoran@Sun.COM int ret; 1386*8212SMichael.Corcoran@Sun.COM caddr_t myaddr; 1387*8212SMichael.Corcoran@Sun.COM size_t mylen; 1388*8212SMichael.Corcoran@Sun.COM struct seg *seg; 1389*8212SMichael.Corcoran@Sun.COM 1390*8212SMichael.Corcoran@Sun.COM /* No need to reserve swap space now since it will be reserved later */ 1391*8212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 1392*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 1393*8212SMichael.Corcoran@Sun.COM for (i = 0; i < loadable; i++) { 1394*8212SMichael.Corcoran@Sun.COM 1395*8212SMichael.Corcoran@Sun.COM myaddr = start_addr + (size_t)mrp[i].mr_addr; 1396*8212SMichael.Corcoran@Sun.COM mylen = mrp[i].mr_msize; 1397*8212SMichael.Corcoran@Sun.COM 1398*8212SMichael.Corcoran@Sun.COM /* See if there is a hole in the as for this range */ 1399*8212SMichael.Corcoran@Sun.COM if (as_gap(as, mylen, &myaddr, &mylen, 0, NULL) == 0) { 1400*8212SMichael.Corcoran@Sun.COM ASSERT(myaddr == start_addr + (size_t)mrp[i].mr_addr); 1401*8212SMichael.Corcoran@Sun.COM ASSERT(mylen == mrp[i].mr_msize); 1402*8212SMichael.Corcoran@Sun.COM 1403*8212SMichael.Corcoran@Sun.COM #ifdef DEBUG 1404*8212SMichael.Corcoran@Sun.COM if (MR_GET_TYPE(mrp[i].mr_flags) == MR_PADDING) { 1405*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_padding); 1406*8212SMichael.Corcoran@Sun.COM } 1407*8212SMichael.Corcoran@Sun.COM #endif 1408*8212SMichael.Corcoran@Sun.COM ret = as_map(as, myaddr, mylen, segvn_create, &crargs); 1409*8212SMichael.Corcoran@Sun.COM if (ret) { 1410*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1411*8212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i, start_addr); 1412*8212SMichael.Corcoran@Sun.COM return (ret); 1413*8212SMichael.Corcoran@Sun.COM } 1414*8212SMichael.Corcoran@Sun.COM } else { 1415*8212SMichael.Corcoran@Sun.COM /* 1416*8212SMichael.Corcoran@Sun.COM * There is a mapping that exists in the range 1417*8212SMichael.Corcoran@Sun.COM * so check to see if it was a "reservation" 1418*8212SMichael.Corcoran@Sun.COM * from /dev/null. The mapping is from 1419*8212SMichael.Corcoran@Sun.COM * /dev/null if the mapping comes from 1420*8212SMichael.Corcoran@Sun.COM * segdev and the type is neither MAP_SHARED 1421*8212SMichael.Corcoran@Sun.COM * nor MAP_PRIVATE. 1422*8212SMichael.Corcoran@Sun.COM */ 1423*8212SMichael.Corcoran@Sun.COM AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 1424*8212SMichael.Corcoran@Sun.COM seg = as_findseg(as, myaddr, 0); 1425*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_mapped); 1426*8212SMichael.Corcoran@Sun.COM if (seg && seg->s_ops == &segdev_ops && 1427*8212SMichael.Corcoran@Sun.COM ((SEGOP_GETTYPE(seg, myaddr) & 1428*8212SMichael.Corcoran@Sun.COM (MAP_SHARED | MAP_PRIVATE)) == 0) && 1429*8212SMichael.Corcoran@Sun.COM myaddr >= seg->s_base && 1430*8212SMichael.Corcoran@Sun.COM myaddr + mylen <= 1431*8212SMichael.Corcoran@Sun.COM seg->s_base + seg->s_size) { 1432*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_devnull); 1433*8212SMichael.Corcoran@Sun.COM AS_LOCK_EXIT(as, &as->a_lock); 1434*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, myaddr, mylen); 1435*8212SMichael.Corcoran@Sun.COM ret = as_map(as, myaddr, mylen, segvn_create, 1436*8212SMichael.Corcoran@Sun.COM &crargs); 1437*8212SMichael.Corcoran@Sun.COM mrp[i].mr_flags |= MR_RESV; 1438*8212SMichael.Corcoran@Sun.COM if (ret) { 1439*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1440*8212SMichael.Corcoran@Sun.COM /* Need to remap what we unmapped */ 1441*8212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i + 1, 1442*8212SMichael.Corcoran@Sun.COM start_addr); 1443*8212SMichael.Corcoran@Sun.COM return (ret); 1444*8212SMichael.Corcoran@Sun.COM } 1445*8212SMichael.Corcoran@Sun.COM } else { 1446*8212SMichael.Corcoran@Sun.COM AS_LOCK_EXIT(as, &as->a_lock); 1447*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1448*8212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i, start_addr); 1449*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_in_use); 1450*8212SMichael.Corcoran@Sun.COM return (EADDRINUSE); 1451*8212SMichael.Corcoran@Sun.COM } 1452*8212SMichael.Corcoran@Sun.COM } 1453*8212SMichael.Corcoran@Sun.COM } 1454*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 1455*8212SMichael.Corcoran@Sun.COM return (0); 1456*8212SMichael.Corcoran@Sun.COM } 1457*8212SMichael.Corcoran@Sun.COM 1458*8212SMichael.Corcoran@Sun.COM /* 1459*8212SMichael.Corcoran@Sun.COM * Walk through the ELF program headers and extract all useful information 1460*8212SMichael.Corcoran@Sun.COM * for PT_LOAD and PT_SUNWBSS segments into mrp. 1461*8212SMichael.Corcoran@Sun.COM * Return 0 on success or error on failure. 1462*8212SMichael.Corcoran@Sun.COM */ 1463*8212SMichael.Corcoran@Sun.COM static int 1464*8212SMichael.Corcoran@Sun.COM process_phdr(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, mmapobj_result_t *mrp, 1465*8212SMichael.Corcoran@Sun.COM vnode_t *vp, uint_t *num_mapped, size_t padding, cred_t *fcred) 1466*8212SMichael.Corcoran@Sun.COM { 1467*8212SMichael.Corcoran@Sun.COM int i; 1468*8212SMichael.Corcoran@Sun.COM caddr_t start_addr = NULL; 1469*8212SMichael.Corcoran@Sun.COM caddr_t vaddr; 1470*8212SMichael.Corcoran@Sun.COM size_t len = 0; 1471*8212SMichael.Corcoran@Sun.COM size_t lib_len = 0; 1472*8212SMichael.Corcoran@Sun.COM int ret; 1473*8212SMichael.Corcoran@Sun.COM int prot; 1474*8212SMichael.Corcoran@Sun.COM struct lib_va *lvp = NULL; 1475*8212SMichael.Corcoran@Sun.COM vattr_t vattr; 1476*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 1477*8212SMichael.Corcoran@Sun.COM int error; 1478*8212SMichael.Corcoran@Sun.COM int loadable = 0; 1479*8212SMichael.Corcoran@Sun.COM int current = 0; 1480*8212SMichael.Corcoran@Sun.COM int use_lib_va = 1; 1481*8212SMichael.Corcoran@Sun.COM size_t align = 0; 1482*8212SMichael.Corcoran@Sun.COM size_t add_pad = 0; 1483*8212SMichael.Corcoran@Sun.COM int hdr_seen = 0; 1484*8212SMichael.Corcoran@Sun.COM ushort_t e_type = ehdrp->e_type; /* same offset 32 and 64 bit */ 1485*8212SMichael.Corcoran@Sun.COM uint_t p_type; 1486*8212SMichael.Corcoran@Sun.COM offset_t p_offset; 1487*8212SMichael.Corcoran@Sun.COM size_t p_memsz; 1488*8212SMichael.Corcoran@Sun.COM size_t p_filesz; 1489*8212SMichael.Corcoran@Sun.COM uint_t p_flags; 1490*8212SMichael.Corcoran@Sun.COM int hsize; 1491*8212SMichael.Corcoran@Sun.COM model_t model; 1492*8212SMichael.Corcoran@Sun.COM STRUCT_HANDLE(myphdr, mph); 1493*8212SMichael.Corcoran@Sun.COM 1494*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 1495*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 1496*8212SMichael.Corcoran@Sun.COM 1497*8212SMichael.Corcoran@Sun.COM /* 1498*8212SMichael.Corcoran@Sun.COM * Need to make sure that hsize is aligned properly. 1499*8212SMichael.Corcoran@Sun.COM * For 32bit processes, 4 byte alignment is required. 1500*8212SMichael.Corcoran@Sun.COM * For 64bit processes, 8 byte alignment is required. 1501*8212SMichael.Corcoran@Sun.COM * If the alignment isn't correct, we need to return failure 1502*8212SMichael.Corcoran@Sun.COM * since it could cause an alignment error panic while walking 1503*8212SMichael.Corcoran@Sun.COM * the phdr array. 1504*8212SMichael.Corcoran@Sun.COM */ 1505*8212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 1506*8212SMichael.Corcoran@Sun.COM hsize = ehdrp->e_phentsize; 1507*8212SMichael.Corcoran@Sun.COM if (hsize & 7) { 1508*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent_align64); 1509*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1510*8212SMichael.Corcoran@Sun.COM } 1511*8212SMichael.Corcoran@Sun.COM } else { 1512*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 1513*8212SMichael.Corcoran@Sun.COM hsize = ((Elf32_Ehdr *)ehdrp)->e_phentsize; 1514*8212SMichael.Corcoran@Sun.COM if (hsize & 3) { 1515*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent_align32); 1516*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1517*8212SMichael.Corcoran@Sun.COM } 1518*8212SMichael.Corcoran@Sun.COM } 1519*8212SMichael.Corcoran@Sun.COM 1520*8212SMichael.Corcoran@Sun.COM if (padding != 0) { 1521*8212SMichael.Corcoran@Sun.COM use_lib_va = 0; 1522*8212SMichael.Corcoran@Sun.COM } 1523*8212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN) { 1524*8212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_FSID | AT_NODEID | AT_CTIME | AT_MTIME; 1525*8212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 1526*8212SMichael.Corcoran@Sun.COM if (error) { 1527*8212SMichael.Corcoran@Sun.COM return (error); 1528*8212SMichael.Corcoran@Sun.COM } 1529*8212SMichael.Corcoran@Sun.COM /* Check to see if we already have a description for this lib */ 1530*8212SMichael.Corcoran@Sun.COM lvp = lib_va_find(&vattr); 1531*8212SMichael.Corcoran@Sun.COM 1532*8212SMichael.Corcoran@Sun.COM if (lvp != NULL) { 1533*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_found); 1534*8212SMichael.Corcoran@Sun.COM if (use_lib_va) { 1535*8212SMichael.Corcoran@Sun.COM start_addr = mmapobj_lookup_start_addr(lvp); 1536*8212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 1537*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1538*8212SMichael.Corcoran@Sun.COM return (ENOMEM); 1539*8212SMichael.Corcoran@Sun.COM } 1540*8212SMichael.Corcoran@Sun.COM } 1541*8212SMichael.Corcoran@Sun.COM 1542*8212SMichael.Corcoran@Sun.COM /* 1543*8212SMichael.Corcoran@Sun.COM * loadable may be zero if the original allocator 1544*8212SMichael.Corcoran@Sun.COM * of lvp hasn't finished setting it up but the rest 1545*8212SMichael.Corcoran@Sun.COM * of the fields will be accurate. 1546*8212SMichael.Corcoran@Sun.COM */ 1547*8212SMichael.Corcoran@Sun.COM loadable = lvp->lv_num_segs; 1548*8212SMichael.Corcoran@Sun.COM len = lvp->lv_len; 1549*8212SMichael.Corcoran@Sun.COM align = lvp->lv_align; 1550*8212SMichael.Corcoran@Sun.COM } 1551*8212SMichael.Corcoran@Sun.COM } 1552*8212SMichael.Corcoran@Sun.COM 1553*8212SMichael.Corcoran@Sun.COM /* 1554*8212SMichael.Corcoran@Sun.COM * Determine the span of all loadable segments and calculate the 1555*8212SMichael.Corcoran@Sun.COM * number of loadable segments, the total len spanned by the mappings 1556*8212SMichael.Corcoran@Sun.COM * and the max alignment, if we didn't get them above. 1557*8212SMichael.Corcoran@Sun.COM */ 1558*8212SMichael.Corcoran@Sun.COM if (loadable == 0) { 1559*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_loadable_yet); 1560*8212SMichael.Corcoran@Sun.COM ret = calc_loadable(ehdrp, phdrbase, nphdrs, &len, 1561*8212SMichael.Corcoran@Sun.COM &loadable, &align); 1562*8212SMichael.Corcoran@Sun.COM if (ret != 0) { 1563*8212SMichael.Corcoran@Sun.COM /* 1564*8212SMichael.Corcoran@Sun.COM * Since it'd be an invalid file, we shouldn't have 1565*8212SMichael.Corcoran@Sun.COM * cached it previously. 1566*8212SMichael.Corcoran@Sun.COM */ 1567*8212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 1568*8212SMichael.Corcoran@Sun.COM return (ret); 1569*8212SMichael.Corcoran@Sun.COM } 1570*8212SMichael.Corcoran@Sun.COM #ifdef DEBUG 1571*8212SMichael.Corcoran@Sun.COM if (lvp) { 1572*8212SMichael.Corcoran@Sun.COM ASSERT(len == lvp->lv_len); 1573*8212SMichael.Corcoran@Sun.COM ASSERT(align == lvp->lv_align); 1574*8212SMichael.Corcoran@Sun.COM } 1575*8212SMichael.Corcoran@Sun.COM #endif 1576*8212SMichael.Corcoran@Sun.COM } 1577*8212SMichael.Corcoran@Sun.COM 1578*8212SMichael.Corcoran@Sun.COM /* Make sure there's something to map. */ 1579*8212SMichael.Corcoran@Sun.COM if (len == 0 || loadable == 0) { 1580*8212SMichael.Corcoran@Sun.COM /* 1581*8212SMichael.Corcoran@Sun.COM * Since it'd be an invalid file, we shouldn't have 1582*8212SMichael.Corcoran@Sun.COM * cached it previously. 1583*8212SMichael.Corcoran@Sun.COM */ 1584*8212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 1585*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(nothing_to_map); 1586*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1587*8212SMichael.Corcoran@Sun.COM } 1588*8212SMichael.Corcoran@Sun.COM 1589*8212SMichael.Corcoran@Sun.COM lib_len = len; 1590*8212SMichael.Corcoran@Sun.COM if (padding != 0) { 1591*8212SMichael.Corcoran@Sun.COM loadable += 2; 1592*8212SMichael.Corcoran@Sun.COM } 1593*8212SMichael.Corcoran@Sun.COM if (loadable > *num_mapped) { 1594*8212SMichael.Corcoran@Sun.COM *num_mapped = loadable; 1595*8212SMichael.Corcoran@Sun.COM /* cleanup previous reservation */ 1596*8212SMichael.Corcoran@Sun.COM if (start_addr) { 1597*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, lib_len); 1598*8212SMichael.Corcoran@Sun.COM } 1599*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(e2big); 1600*8212SMichael.Corcoran@Sun.COM if (lvp) { 1601*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1602*8212SMichael.Corcoran@Sun.COM } 1603*8212SMichael.Corcoran@Sun.COM return (E2BIG); 1604*8212SMichael.Corcoran@Sun.COM } 1605*8212SMichael.Corcoran@Sun.COM 1606*8212SMichael.Corcoran@Sun.COM /* 1607*8212SMichael.Corcoran@Sun.COM * We now know the size of the object to map and now we need to 1608*8212SMichael.Corcoran@Sun.COM * get the start address to map it at. It's possible we already 1609*8212SMichael.Corcoran@Sun.COM * have it if we found all the info we need in the lib_va cache. 1610*8212SMichael.Corcoran@Sun.COM */ 1611*8212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN && start_addr == NULL) { 1612*8212SMichael.Corcoran@Sun.COM /* 1613*8212SMichael.Corcoran@Sun.COM * Need to make sure padding does not throw off 1614*8212SMichael.Corcoran@Sun.COM * required alignment. We can only specify an 1615*8212SMichael.Corcoran@Sun.COM * alignment for the starting address to be mapped, 1616*8212SMichael.Corcoran@Sun.COM * so we round padding up to the alignment and map 1617*8212SMichael.Corcoran@Sun.COM * from there and then throw out the extra later. 1618*8212SMichael.Corcoran@Sun.COM */ 1619*8212SMichael.Corcoran@Sun.COM if (padding != 0) { 1620*8212SMichael.Corcoran@Sun.COM if (align > 1) { 1621*8212SMichael.Corcoran@Sun.COM add_pad = P2ROUNDUP(padding, align); 1622*8212SMichael.Corcoran@Sun.COM len += add_pad; 1623*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(dyn_pad_align); 1624*8212SMichael.Corcoran@Sun.COM } else { 1625*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(dyn_pad_noalign); 1626*8212SMichael.Corcoran@Sun.COM len += padding; /* at beginning */ 1627*8212SMichael.Corcoran@Sun.COM } 1628*8212SMichael.Corcoran@Sun.COM len += padding; /* at end of mapping */ 1629*8212SMichael.Corcoran@Sun.COM } 1630*8212SMichael.Corcoran@Sun.COM /* 1631*8212SMichael.Corcoran@Sun.COM * At this point, if lvp is non-NULL, then above we 1632*8212SMichael.Corcoran@Sun.COM * already found it in the cache but did not get 1633*8212SMichael.Corcoran@Sun.COM * the start address since we were not going to use lib_va. 1634*8212SMichael.Corcoran@Sun.COM * Since we know that lib_va will not be used, it's safe 1635*8212SMichael.Corcoran@Sun.COM * to call mmapobj_alloc_start_addr and know that lvp 1636*8212SMichael.Corcoran@Sun.COM * will not be modified. 1637*8212SMichael.Corcoran@Sun.COM */ 1638*8212SMichael.Corcoran@Sun.COM ASSERT(lvp ? use_lib_va == 0 : 1); 1639*8212SMichael.Corcoran@Sun.COM start_addr = mmapobj_alloc_start_addr(&lvp, len, 1640*8212SMichael.Corcoran@Sun.COM use_lib_va, align, &vattr); 1641*8212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 1642*8212SMichael.Corcoran@Sun.COM if (lvp) { 1643*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1644*8212SMichael.Corcoran@Sun.COM } 1645*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_start_fail); 1646*8212SMichael.Corcoran@Sun.COM return (ENOMEM); 1647*8212SMichael.Corcoran@Sun.COM } 1648*8212SMichael.Corcoran@Sun.COM /* 1649*8212SMichael.Corcoran@Sun.COM * If we can't cache it, no need to hang on to it. 1650*8212SMichael.Corcoran@Sun.COM * Setting lv_num_segs to non-zero will make that 1651*8212SMichael.Corcoran@Sun.COM * field active and since there are too many segments 1652*8212SMichael.Corcoran@Sun.COM * to cache, all future users will not try to use lv_mps. 1653*8212SMichael.Corcoran@Sun.COM */ 1654*8212SMichael.Corcoran@Sun.COM if (lvp != NULL && loadable > LIBVA_CACHED_SEGS && use_lib_va) { 1655*8212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = loadable; 1656*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1657*8212SMichael.Corcoran@Sun.COM lvp = NULL; 1658*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_nocache); 1659*8212SMichael.Corcoran@Sun.COM } 1660*8212SMichael.Corcoran@Sun.COM /* 1661*8212SMichael.Corcoran@Sun.COM * Free the beginning of the mapping if the padding 1662*8212SMichael.Corcoran@Sun.COM * was not aligned correctly. 1663*8212SMichael.Corcoran@Sun.COM */ 1664*8212SMichael.Corcoran@Sun.COM if (padding != 0 && add_pad != padding) { 1665*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, 1666*8212SMichael.Corcoran@Sun.COM add_pad - padding); 1667*8212SMichael.Corcoran@Sun.COM start_addr += (add_pad - padding); 1668*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(extra_padding); 1669*8212SMichael.Corcoran@Sun.COM } 1670*8212SMichael.Corcoran@Sun.COM } 1671*8212SMichael.Corcoran@Sun.COM 1672*8212SMichael.Corcoran@Sun.COM /* 1673*8212SMichael.Corcoran@Sun.COM * At this point, we have reserved the virtual address space 1674*8212SMichael.Corcoran@Sun.COM * for our mappings. Now we need to start filling out the mrp 1675*8212SMichael.Corcoran@Sun.COM * array to describe all of the individual mappings we are going 1676*8212SMichael.Corcoran@Sun.COM * to return. 1677*8212SMichael.Corcoran@Sun.COM * For ET_EXEC there has been no memory reservation since we are 1678*8212SMichael.Corcoran@Sun.COM * using fixed addresses. While filling in the mrp array below, 1679*8212SMichael.Corcoran@Sun.COM * we will have the first segment biased to start at addr 0 1680*8212SMichael.Corcoran@Sun.COM * and the rest will be biased by this same amount. Thus if there 1681*8212SMichael.Corcoran@Sun.COM * is padding, the first padding will start at addr 0, and the next 1682*8212SMichael.Corcoran@Sun.COM * segment will start at the value of padding. 1683*8212SMichael.Corcoran@Sun.COM */ 1684*8212SMichael.Corcoran@Sun.COM 1685*8212SMichael.Corcoran@Sun.COM /* We'll fill out padding later, so start filling in mrp at index 1 */ 1686*8212SMichael.Corcoran@Sun.COM if (padding != 0) { 1687*8212SMichael.Corcoran@Sun.COM current = 1; 1688*8212SMichael.Corcoran@Sun.COM } 1689*8212SMichael.Corcoran@Sun.COM 1690*8212SMichael.Corcoran@Sun.COM /* If we have no more need for lvp let it go now */ 1691*8212SMichael.Corcoran@Sun.COM if (lvp != NULL && use_lib_va == 0) { 1692*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1693*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_not_needed); 1694*8212SMichael.Corcoran@Sun.COM lvp = NULL; 1695*8212SMichael.Corcoran@Sun.COM } 1696*8212SMichael.Corcoran@Sun.COM 1697*8212SMichael.Corcoran@Sun.COM /* Now fill out the mrp structs from the program headers */ 1698*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 1699*8212SMichael.Corcoran@Sun.COM for (i = 0; i < nphdrs; i++) { 1700*8212SMichael.Corcoran@Sun.COM p_type = STRUCT_FGET(mph, x.p_type); 1701*8212SMichael.Corcoran@Sun.COM if (p_type == PT_LOAD || p_type == PT_SUNWBSS) { 1702*8212SMichael.Corcoran@Sun.COM vaddr = (caddr_t)(uintptr_t)STRUCT_FGET(mph, x.p_vaddr); 1703*8212SMichael.Corcoran@Sun.COM p_memsz = STRUCT_FGET(mph, x.p_memsz); 1704*8212SMichael.Corcoran@Sun.COM p_filesz = STRUCT_FGET(mph, x.p_filesz); 1705*8212SMichael.Corcoran@Sun.COM p_offset = STRUCT_FGET(mph, x.p_offset); 1706*8212SMichael.Corcoran@Sun.COM p_flags = STRUCT_FGET(mph, x.p_flags); 1707*8212SMichael.Corcoran@Sun.COM 1708*8212SMichael.Corcoran@Sun.COM /* 1709*8212SMichael.Corcoran@Sun.COM * Skip this header if it requests no memory to be 1710*8212SMichael.Corcoran@Sun.COM * mapped. 1711*8212SMichael.Corcoran@Sun.COM */ 1712*8212SMichael.Corcoran@Sun.COM if (p_memsz == 0) { 1713*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 1714*8212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 1715*8212SMichael.Corcoran@Sun.COM hsize)); 1716*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_mem_map_sz); 1717*8212SMichael.Corcoran@Sun.COM continue; 1718*8212SMichael.Corcoran@Sun.COM } 1719*8212SMichael.Corcoran@Sun.COM 1720*8212SMichael.Corcoran@Sun.COM prot = 0; 1721*8212SMichael.Corcoran@Sun.COM if (p_flags & PF_R) 1722*8212SMichael.Corcoran@Sun.COM prot |= PROT_READ; 1723*8212SMichael.Corcoran@Sun.COM if (p_flags & PF_W) 1724*8212SMichael.Corcoran@Sun.COM prot |= PROT_WRITE; 1725*8212SMichael.Corcoran@Sun.COM if (p_flags & PF_X) 1726*8212SMichael.Corcoran@Sun.COM prot |= PROT_EXEC; 1727*8212SMichael.Corcoran@Sun.COM 1728*8212SMichael.Corcoran@Sun.COM ASSERT(current < loadable); 1729*8212SMichael.Corcoran@Sun.COM mrp[current].mr_msize = p_memsz; 1730*8212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize = p_filesz; 1731*8212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = p_offset; 1732*8212SMichael.Corcoran@Sun.COM mrp[current].mr_prot = prot; 1733*8212SMichael.Corcoran@Sun.COM 1734*8212SMichael.Corcoran@Sun.COM if (hdr_seen == 0 && p_filesz != 0) { 1735*8212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = MR_HDR_ELF; 1736*8212SMichael.Corcoran@Sun.COM /* 1737*8212SMichael.Corcoran@Sun.COM * We modify mr_addr and mr_offset because we 1738*8212SMichael.Corcoran@Sun.COM * need to map the ELF header as well, and if 1739*8212SMichael.Corcoran@Sun.COM * we didn't then the header could be left out 1740*8212SMichael.Corcoran@Sun.COM * of the mapping that we will create later. 1741*8212SMichael.Corcoran@Sun.COM * Since we're removing the offset, we need to 1742*8212SMichael.Corcoran@Sun.COM * account for that in the other fields as well 1743*8212SMichael.Corcoran@Sun.COM * since we will be mapping the memory from 0 1744*8212SMichael.Corcoran@Sun.COM * to p_offset. 1745*8212SMichael.Corcoran@Sun.COM */ 1746*8212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN) { 1747*8212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = 0; 1748*8212SMichael.Corcoran@Sun.COM mrp[current].mr_msize += p_offset; 1749*8212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize += p_offset; 1750*8212SMichael.Corcoran@Sun.COM } else { 1751*8212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_EXEC); 1752*8212SMichael.Corcoran@Sun.COM /* 1753*8212SMichael.Corcoran@Sun.COM * Save off the start addr which will be 1754*8212SMichael.Corcoran@Sun.COM * our bias for the rest of the 1755*8212SMichael.Corcoran@Sun.COM * ET_EXEC mappings. 1756*8212SMichael.Corcoran@Sun.COM */ 1757*8212SMichael.Corcoran@Sun.COM start_addr = vaddr - padding; 1758*8212SMichael.Corcoran@Sun.COM } 1759*8212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = (caddr_t)padding; 1760*8212SMichael.Corcoran@Sun.COM hdr_seen = 1; 1761*8212SMichael.Corcoran@Sun.COM } else { 1762*8212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 1763*8212SMichael.Corcoran@Sun.COM /* bias mr_addr */ 1764*8212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = 1765*8212SMichael.Corcoran@Sun.COM vaddr - (size_t)start_addr; 1766*8212SMichael.Corcoran@Sun.COM } else { 1767*8212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = vaddr + padding; 1768*8212SMichael.Corcoran@Sun.COM } 1769*8212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = 0; 1770*8212SMichael.Corcoran@Sun.COM } 1771*8212SMichael.Corcoran@Sun.COM current++; 1772*8212SMichael.Corcoran@Sun.COM } 1773*8212SMichael.Corcoran@Sun.COM 1774*8212SMichael.Corcoran@Sun.COM /* Move to next phdr */ 1775*8212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 1776*8212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 1777*8212SMichael.Corcoran@Sun.COM hsize)); 1778*8212SMichael.Corcoran@Sun.COM } 1779*8212SMichael.Corcoran@Sun.COM 1780*8212SMichael.Corcoran@Sun.COM /* Now fill out the padding segments */ 1781*8212SMichael.Corcoran@Sun.COM if (padding != 0) { 1782*8212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = NULL; 1783*8212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = padding; 1784*8212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = 0; 1785*8212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 1786*8212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = 0; 1787*8212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_PADDING; 1788*8212SMichael.Corcoran@Sun.COM 1789*8212SMichael.Corcoran@Sun.COM /* Setup padding for the last segment */ 1790*8212SMichael.Corcoran@Sun.COM ASSERT(current == loadable - 1); 1791*8212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = (caddr_t)lib_len + padding; 1792*8212SMichael.Corcoran@Sun.COM mrp[current].mr_msize = padding; 1793*8212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize = 0; 1794*8212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = 0; 1795*8212SMichael.Corcoran@Sun.COM mrp[current].mr_prot = 0; 1796*8212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = MR_PADDING; 1797*8212SMichael.Corcoran@Sun.COM } 1798*8212SMichael.Corcoran@Sun.COM 1799*8212SMichael.Corcoran@Sun.COM /* 1800*8212SMichael.Corcoran@Sun.COM * Need to make sure address ranges desired are not in use or 1801*8212SMichael.Corcoran@Sun.COM * are previously allocated reservations from /dev/null. For 1802*8212SMichael.Corcoran@Sun.COM * ET_DYN, we already made sure our address range was free. 1803*8212SMichael.Corcoran@Sun.COM */ 1804*8212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 1805*8212SMichael.Corcoran@Sun.COM ret = check_exec_addrs(loadable, mrp, start_addr); 1806*8212SMichael.Corcoran@Sun.COM if (ret != 0) { 1807*8212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 1808*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(check_exec_failed); 1809*8212SMichael.Corcoran@Sun.COM return (ret); 1810*8212SMichael.Corcoran@Sun.COM } 1811*8212SMichael.Corcoran@Sun.COM } 1812*8212SMichael.Corcoran@Sun.COM 1813*8212SMichael.Corcoran@Sun.COM /* Finish up our business with lvp. */ 1814*8212SMichael.Corcoran@Sun.COM if (lvp) { 1815*8212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_DYN); 1816*8212SMichael.Corcoran@Sun.COM if (lvp->lv_num_segs == 0 && loadable <= LIBVA_CACHED_SEGS) { 1817*8212SMichael.Corcoran@Sun.COM bcopy(mrp, lvp->lv_mps, 1818*8212SMichael.Corcoran@Sun.COM loadable * sizeof (mmapobj_result_t)); 1819*8212SMichael.Corcoran@Sun.COM membar_producer(); 1820*8212SMichael.Corcoran@Sun.COM } 1821*8212SMichael.Corcoran@Sun.COM /* 1822*8212SMichael.Corcoran@Sun.COM * Setting lv_num_segs to a non-zero value indicates that 1823*8212SMichael.Corcoran@Sun.COM * lv_mps is now valid and can be used by other threads. 1824*8212SMichael.Corcoran@Sun.COM * So, the above stores need to finish before lv_num_segs 1825*8212SMichael.Corcoran@Sun.COM * is updated. lv_mps is only valid if lv_num_segs is 1826*8212SMichael.Corcoran@Sun.COM * greater than LIBVA_CACHED_SEGS. 1827*8212SMichael.Corcoran@Sun.COM */ 1828*8212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = loadable; 1829*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 1830*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_used); 1831*8212SMichael.Corcoran@Sun.COM } 1832*8212SMichael.Corcoran@Sun.COM 1833*8212SMichael.Corcoran@Sun.COM /* Now that we have mrp completely filled out go map it */ 1834*8212SMichael.Corcoran@Sun.COM ret = mmapobj_map_elf(vp, start_addr, mrp, loadable, fcred, e_type); 1835*8212SMichael.Corcoran@Sun.COM if (ret == 0) { 1836*8212SMichael.Corcoran@Sun.COM *num_mapped = loadable; 1837*8212SMichael.Corcoran@Sun.COM } 1838*8212SMichael.Corcoran@Sun.COM 1839*8212SMichael.Corcoran@Sun.COM return (ret); 1840*8212SMichael.Corcoran@Sun.COM } 1841*8212SMichael.Corcoran@Sun.COM 1842*8212SMichael.Corcoran@Sun.COM /* 1843*8212SMichael.Corcoran@Sun.COM * Take the ELF file passed in, and do the work of mapping it. 1844*8212SMichael.Corcoran@Sun.COM * num_mapped in - # elements in user buffer 1845*8212SMichael.Corcoran@Sun.COM * num_mapped out - # sections mapped and length of mrp array if 1846*8212SMichael.Corcoran@Sun.COM * no errors. 1847*8212SMichael.Corcoran@Sun.COM */ 1848*8212SMichael.Corcoran@Sun.COM static int 1849*8212SMichael.Corcoran@Sun.COM doelfwork(Ehdr *ehdrp, vnode_t *vp, mmapobj_result_t *mrp, 1850*8212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 1851*8212SMichael.Corcoran@Sun.COM { 1852*8212SMichael.Corcoran@Sun.COM int error; 1853*8212SMichael.Corcoran@Sun.COM offset_t phoff; 1854*8212SMichael.Corcoran@Sun.COM int nphdrs; 1855*8212SMichael.Corcoran@Sun.COM unsigned char ei_class; 1856*8212SMichael.Corcoran@Sun.COM unsigned short phentsize; 1857*8212SMichael.Corcoran@Sun.COM ssize_t phsizep; 1858*8212SMichael.Corcoran@Sun.COM caddr_t phbasep; 1859*8212SMichael.Corcoran@Sun.COM int to_map; 1860*8212SMichael.Corcoran@Sun.COM model_t model; 1861*8212SMichael.Corcoran@Sun.COM 1862*8212SMichael.Corcoran@Sun.COM ei_class = ehdrp->e_ident[EI_CLASS]; 1863*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 1864*8212SMichael.Corcoran@Sun.COM if ((model == DATAMODEL_ILP32 && ei_class == ELFCLASS64) || 1865*8212SMichael.Corcoran@Sun.COM (model == DATAMODEL_LP64 && ei_class == ELFCLASS32)) { 1866*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(wrong_model); 1867*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1868*8212SMichael.Corcoran@Sun.COM } 1869*8212SMichael.Corcoran@Sun.COM 1870*8212SMichael.Corcoran@Sun.COM /* Can't execute code from "noexec" mounted filesystem. */ 1871*8212SMichael.Corcoran@Sun.COM if (ehdrp->e_type == ET_EXEC && 1872*8212SMichael.Corcoran@Sun.COM (vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) { 1873*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(noexec_fs); 1874*8212SMichael.Corcoran@Sun.COM return (EACCES); 1875*8212SMichael.Corcoran@Sun.COM } 1876*8212SMichael.Corcoran@Sun.COM 1877*8212SMichael.Corcoran@Sun.COM /* 1878*8212SMichael.Corcoran@Sun.COM * Relocatable and core files are mapped as a single flat file 1879*8212SMichael.Corcoran@Sun.COM * since no interpretation is done on them by mmapobj. 1880*8212SMichael.Corcoran@Sun.COM */ 1881*8212SMichael.Corcoran@Sun.COM if (ehdrp->e_type == ET_REL || ehdrp->e_type == ET_CORE) { 1882*8212SMichael.Corcoran@Sun.COM to_map = padding ? 3 : 1; 1883*8212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 1884*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 1885*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(e2big_et_rel); 1886*8212SMichael.Corcoran@Sun.COM return (E2BIG); 1887*8212SMichael.Corcoran@Sun.COM } 1888*8212SMichael.Corcoran@Sun.COM error = mmapobj_map_flat(vp, mrp, padding, fcred); 1889*8212SMichael.Corcoran@Sun.COM if (error == 0) { 1890*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 1891*8212SMichael.Corcoran@Sun.COM mrp[padding ? 1 : 0].mr_flags = MR_HDR_ELF; 1892*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(et_rel_mapped); 1893*8212SMichael.Corcoran@Sun.COM } 1894*8212SMichael.Corcoran@Sun.COM return (error); 1895*8212SMichael.Corcoran@Sun.COM } 1896*8212SMichael.Corcoran@Sun.COM 1897*8212SMichael.Corcoran@Sun.COM /* Check for an unknown ELF type */ 1898*8212SMichael.Corcoran@Sun.COM if (ehdrp->e_type != ET_EXEC && ehdrp->e_type != ET_DYN) { 1899*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unknown_elf_type); 1900*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1901*8212SMichael.Corcoran@Sun.COM } 1902*8212SMichael.Corcoran@Sun.COM 1903*8212SMichael.Corcoran@Sun.COM if (ei_class == ELFCLASS32) { 1904*8212SMichael.Corcoran@Sun.COM Elf32_Ehdr *e32hdr = (Elf32_Ehdr *)ehdrp; 1905*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 1906*8212SMichael.Corcoran@Sun.COM nphdrs = e32hdr->e_phnum; 1907*8212SMichael.Corcoran@Sun.COM phentsize = e32hdr->e_phentsize; 1908*8212SMichael.Corcoran@Sun.COM if (phentsize < sizeof (Elf32_Phdr)) { 1909*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent32_too_small); 1910*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1911*8212SMichael.Corcoran@Sun.COM } 1912*8212SMichael.Corcoran@Sun.COM phoff = e32hdr->e_phoff; 1913*8212SMichael.Corcoran@Sun.COM } else if (ei_class == ELFCLASS64) { 1914*8212SMichael.Corcoran@Sun.COM Elf64_Ehdr *e64hdr = (Elf64_Ehdr *)ehdrp; 1915*8212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_LP64); 1916*8212SMichael.Corcoran@Sun.COM nphdrs = e64hdr->e_phnum; 1917*8212SMichael.Corcoran@Sun.COM phentsize = e64hdr->e_phentsize; 1918*8212SMichael.Corcoran@Sun.COM if (phentsize < sizeof (Elf64_Phdr)) { 1919*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent64_too_small); 1920*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1921*8212SMichael.Corcoran@Sun.COM } 1922*8212SMichael.Corcoran@Sun.COM phoff = e64hdr->e_phoff; 1923*8212SMichael.Corcoran@Sun.COM } else { 1924*8212SMichael.Corcoran@Sun.COM /* fallthrough case for an invalid ELF class */ 1925*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(inval_elf_class); 1926*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1927*8212SMichael.Corcoran@Sun.COM } 1928*8212SMichael.Corcoran@Sun.COM 1929*8212SMichael.Corcoran@Sun.COM /* 1930*8212SMichael.Corcoran@Sun.COM * nphdrs should only have this value for core files which are handled 1931*8212SMichael.Corcoran@Sun.COM * above as a single mapping. If other file types ever use this 1932*8212SMichael.Corcoran@Sun.COM * sentinel, then we'll add the support needed to handle this here. 1933*8212SMichael.Corcoran@Sun.COM */ 1934*8212SMichael.Corcoran@Sun.COM if (nphdrs == PN_XNUM) { 1935*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(too_many_phdrs); 1936*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1937*8212SMichael.Corcoran@Sun.COM } 1938*8212SMichael.Corcoran@Sun.COM 1939*8212SMichael.Corcoran@Sun.COM phsizep = nphdrs * phentsize; 1940*8212SMichael.Corcoran@Sun.COM 1941*8212SMichael.Corcoran@Sun.COM if (phsizep == 0) { 1942*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_phsize); 1943*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 1944*8212SMichael.Corcoran@Sun.COM } 1945*8212SMichael.Corcoran@Sun.COM 1946*8212SMichael.Corcoran@Sun.COM /* Make sure we only wait for memory if it's a reasonable request */ 1947*8212SMichael.Corcoran@Sun.COM if (phsizep > mmapobj_alloc_threshold) { 1948*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phsize_large); 1949*8212SMichael.Corcoran@Sun.COM if ((phbasep = kmem_alloc(phsizep, KM_NOSLEEP)) == NULL) { 1950*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phsize_xtralarge); 1951*8212SMichael.Corcoran@Sun.COM return (ENOMEM); 1952*8212SMichael.Corcoran@Sun.COM } 1953*8212SMichael.Corcoran@Sun.COM } else { 1954*8212SMichael.Corcoran@Sun.COM phbasep = kmem_alloc(phsizep, KM_SLEEP); 1955*8212SMichael.Corcoran@Sun.COM } 1956*8212SMichael.Corcoran@Sun.COM 1957*8212SMichael.Corcoran@Sun.COM if ((error = vn_rdwr(UIO_READ, vp, phbasep, phsizep, 1958*8212SMichael.Corcoran@Sun.COM (offset_t)phoff, UIO_SYSSPACE, 0, (rlim64_t)0, 1959*8212SMichael.Corcoran@Sun.COM fcred, NULL)) != 0) { 1960*8212SMichael.Corcoran@Sun.COM kmem_free(phbasep, phsizep); 1961*8212SMichael.Corcoran@Sun.COM return (error); 1962*8212SMichael.Corcoran@Sun.COM } 1963*8212SMichael.Corcoran@Sun.COM 1964*8212SMichael.Corcoran@Sun.COM /* Now process the phdr's */ 1965*8212SMichael.Corcoran@Sun.COM error = process_phdr(ehdrp, phbasep, nphdrs, mrp, vp, num_mapped, 1966*8212SMichael.Corcoran@Sun.COM padding, fcred); 1967*8212SMichael.Corcoran@Sun.COM kmem_free(phbasep, phsizep); 1968*8212SMichael.Corcoran@Sun.COM return (error); 1969*8212SMichael.Corcoran@Sun.COM } 1970*8212SMichael.Corcoran@Sun.COM 1971*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 1972*8212SMichael.Corcoran@Sun.COM /* 1973*8212SMichael.Corcoran@Sun.COM * Hack to support 64 bit kernels running AOUT 4.x programs. 1974*8212SMichael.Corcoran@Sun.COM * This is the sizeof (struct nlist) for a 32 bit kernel. 1975*8212SMichael.Corcoran@Sun.COM * Since AOUT programs are 32 bit only, they will never use the 64 bit 1976*8212SMichael.Corcoran@Sun.COM * sizeof (struct nlist) and thus creating a #define is the simplest 1977*8212SMichael.Corcoran@Sun.COM * way around this since this is a format which is not being updated. 1978*8212SMichael.Corcoran@Sun.COM * This will be used in the place of sizeof (struct nlist) below. 1979*8212SMichael.Corcoran@Sun.COM */ 1980*8212SMichael.Corcoran@Sun.COM #define NLIST_SIZE (0xC) 1981*8212SMichael.Corcoran@Sun.COM 1982*8212SMichael.Corcoran@Sun.COM static int 1983*8212SMichael.Corcoran@Sun.COM doaoutwork(vnode_t *vp, mmapobj_result_t *mrp, 1984*8212SMichael.Corcoran@Sun.COM uint_t *num_mapped, struct exec *hdr, cred_t *fcred) 1985*8212SMichael.Corcoran@Sun.COM { 1986*8212SMichael.Corcoran@Sun.COM int error; 1987*8212SMichael.Corcoran@Sun.COM size_t size; 1988*8212SMichael.Corcoran@Sun.COM size_t osize; 1989*8212SMichael.Corcoran@Sun.COM size_t nsize; /* nlist size */ 1990*8212SMichael.Corcoran@Sun.COM size_t msize; 1991*8212SMichael.Corcoran@Sun.COM size_t zfoddiff; 1992*8212SMichael.Corcoran@Sun.COM caddr_t addr; 1993*8212SMichael.Corcoran@Sun.COM caddr_t start_addr; 1994*8212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 1995*8212SMichael.Corcoran@Sun.COM int prot = PROT_USER | PROT_READ | PROT_EXEC; 1996*8212SMichael.Corcoran@Sun.COM uint_t mflag = MAP_PRIVATE | _MAP_LOW32; 1997*8212SMichael.Corcoran@Sun.COM offset_t off = 0; 1998*8212SMichael.Corcoran@Sun.COM int segnum = 0; 1999*8212SMichael.Corcoran@Sun.COM uint_t to_map; 2000*8212SMichael.Corcoran@Sun.COM int is_library = 0; 2001*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 2002*8212SMichael.Corcoran@Sun.COM 2003*8212SMichael.Corcoran@Sun.COM /* Only 32bit apps supported by this file format */ 2004*8212SMichael.Corcoran@Sun.COM if (get_udatamodel() != DATAMODEL_ILP32) { 2005*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_64bit_try); 2006*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 2007*8212SMichael.Corcoran@Sun.COM } 2008*8212SMichael.Corcoran@Sun.COM 2009*8212SMichael.Corcoran@Sun.COM /* Check to see if this is a library */ 2010*8212SMichael.Corcoran@Sun.COM if (hdr->a_magic == ZMAGIC && hdr->a_entry < PAGESIZE) { 2011*8212SMichael.Corcoran@Sun.COM is_library = 1; 2012*8212SMichael.Corcoran@Sun.COM } 2013*8212SMichael.Corcoran@Sun.COM 2014*8212SMichael.Corcoran@Sun.COM /* Can't execute code from "noexec" mounted filesystem. */ 2015*8212SMichael.Corcoran@Sun.COM if (((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) && (is_library == 0)) { 2016*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_noexec); 2017*8212SMichael.Corcoran@Sun.COM return (EACCES); 2018*8212SMichael.Corcoran@Sun.COM } 2019*8212SMichael.Corcoran@Sun.COM 2020*8212SMichael.Corcoran@Sun.COM /* 2021*8212SMichael.Corcoran@Sun.COM * There are 2 ways to calculate the mapped size of executable: 2022*8212SMichael.Corcoran@Sun.COM * 1) rounded text size + data size + bss size. 2023*8212SMichael.Corcoran@Sun.COM * 2) starting offset for text + text size + data size + text relocation 2024*8212SMichael.Corcoran@Sun.COM * size + data relocation size + room for nlist data structure. 2025*8212SMichael.Corcoran@Sun.COM * 2026*8212SMichael.Corcoran@Sun.COM * The larger of the two sizes will be used to map this binary. 2027*8212SMichael.Corcoran@Sun.COM */ 2028*8212SMichael.Corcoran@Sun.COM osize = P2ROUNDUP(hdr->a_text, PAGESIZE) + hdr->a_data + hdr->a_bss; 2029*8212SMichael.Corcoran@Sun.COM 2030*8212SMichael.Corcoran@Sun.COM off = hdr->a_magic == ZMAGIC ? 0 : sizeof (struct exec); 2031*8212SMichael.Corcoran@Sun.COM 2032*8212SMichael.Corcoran@Sun.COM nsize = off + hdr->a_text + hdr->a_data + hdr->a_trsize + 2033*8212SMichael.Corcoran@Sun.COM hdr->a_drsize + NLIST_SIZE; 2034*8212SMichael.Corcoran@Sun.COM 2035*8212SMichael.Corcoran@Sun.COM size = MAX(osize, nsize); 2036*8212SMichael.Corcoran@Sun.COM if (size != nsize) { 2037*8212SMichael.Corcoran@Sun.COM nsize = 0; 2038*8212SMichael.Corcoran@Sun.COM } 2039*8212SMichael.Corcoran@Sun.COM 2040*8212SMichael.Corcoran@Sun.COM /* 2041*8212SMichael.Corcoran@Sun.COM * 1 seg for text and 1 seg for initialized data. 2042*8212SMichael.Corcoran@Sun.COM * 1 seg for bss (if can't fit in leftover space of init data) 2043*8212SMichael.Corcoran@Sun.COM * 1 seg for nlist if needed. 2044*8212SMichael.Corcoran@Sun.COM */ 2045*8212SMichael.Corcoran@Sun.COM to_map = 2 + (nsize ? 1 : 0) + 2046*8212SMichael.Corcoran@Sun.COM (hdr->a_bss > PAGESIZE - P2PHASE(hdr->a_data, PAGESIZE) ? 1 : 0); 2047*8212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 2048*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 2049*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_e2big); 2050*8212SMichael.Corcoran@Sun.COM return (E2BIG); 2051*8212SMichael.Corcoran@Sun.COM } 2052*8212SMichael.Corcoran@Sun.COM 2053*8212SMichael.Corcoran@Sun.COM /* Reserve address space for the whole mapping */ 2054*8212SMichael.Corcoran@Sun.COM if (is_library) { 2055*8212SMichael.Corcoran@Sun.COM /* We'll let VOP_MAP below pick our address for us */ 2056*8212SMichael.Corcoran@Sun.COM addr = NULL; 2057*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_lib); 2058*8212SMichael.Corcoran@Sun.COM } else { 2059*8212SMichael.Corcoran@Sun.COM /* 2060*8212SMichael.Corcoran@Sun.COM * default start address for fixed binaries from AOUT 4.x 2061*8212SMichael.Corcoran@Sun.COM * standard. 2062*8212SMichael.Corcoran@Sun.COM */ 2063*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_fixed); 2064*8212SMichael.Corcoran@Sun.COM mflag |= MAP_FIXED; 2065*8212SMichael.Corcoran@Sun.COM addr = (caddr_t)0x2000; 2066*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 2067*8212SMichael.Corcoran@Sun.COM if (as_gap(as, size, &addr, &size, 0, NULL) != 0) { 2068*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 2069*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_addr_in_use); 2070*8212SMichael.Corcoran@Sun.COM return (EADDRINUSE); 2071*8212SMichael.Corcoran@Sun.COM } 2072*8212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 2073*8212SMichael.Corcoran@Sun.COM error = as_map(as, addr, size, segvn_create, &crargs); 2074*8212SMichael.Corcoran@Sun.COM ASSERT(addr == (caddr_t)0x2000); 2075*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 2076*8212SMichael.Corcoran@Sun.COM } 2077*8212SMichael.Corcoran@Sun.COM 2078*8212SMichael.Corcoran@Sun.COM start_addr = addr; 2079*8212SMichael.Corcoran@Sun.COM osize = size; 2080*8212SMichael.Corcoran@Sun.COM 2081*8212SMichael.Corcoran@Sun.COM /* 2082*8212SMichael.Corcoran@Sun.COM * Map as large as we need, backed by file, this will be text, and 2083*8212SMichael.Corcoran@Sun.COM * possibly the nlist segment. We map over this mapping for bss and 2084*8212SMichael.Corcoran@Sun.COM * initialized data segments. 2085*8212SMichael.Corcoran@Sun.COM */ 2086*8212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, off, as, &addr, size, prot, PROT_ALL, 2087*8212SMichael.Corcoran@Sun.COM mflag, fcred, NULL); 2088*8212SMichael.Corcoran@Sun.COM if (error) { 2089*8212SMichael.Corcoran@Sun.COM if (!is_library) { 2090*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 2091*8212SMichael.Corcoran@Sun.COM } 2092*8212SMichael.Corcoran@Sun.COM return (error); 2093*8212SMichael.Corcoran@Sun.COM } 2094*8212SMichael.Corcoran@Sun.COM 2095*8212SMichael.Corcoran@Sun.COM /* pickup the value of start_addr and osize for libraries */ 2096*8212SMichael.Corcoran@Sun.COM start_addr = addr; 2097*8212SMichael.Corcoran@Sun.COM osize = size; 2098*8212SMichael.Corcoran@Sun.COM 2099*8212SMichael.Corcoran@Sun.COM /* 2100*8212SMichael.Corcoran@Sun.COM * We have our initial reservation/allocation so we need to use fixed 2101*8212SMichael.Corcoran@Sun.COM * addresses from now on. 2102*8212SMichael.Corcoran@Sun.COM */ 2103*8212SMichael.Corcoran@Sun.COM mflag |= MAP_FIXED; 2104*8212SMichael.Corcoran@Sun.COM 2105*8212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = addr; 2106*8212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = hdr->a_text; 2107*8212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = hdr->a_text; 2108*8212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 2109*8212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = PROT_READ | PROT_EXEC; 2110*8212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_HDR_AOUT; 2111*8212SMichael.Corcoran@Sun.COM 2112*8212SMichael.Corcoran@Sun.COM 2113*8212SMichael.Corcoran@Sun.COM /* 2114*8212SMichael.Corcoran@Sun.COM * Map initialized data. We are mapping over a portion of the 2115*8212SMichael.Corcoran@Sun.COM * previous mapping which will be unmapped in VOP_MAP below. 2116*8212SMichael.Corcoran@Sun.COM */ 2117*8212SMichael.Corcoran@Sun.COM off = P2ROUNDUP((offset_t)(hdr->a_text), PAGESIZE); 2118*8212SMichael.Corcoran@Sun.COM msize = off; 2119*8212SMichael.Corcoran@Sun.COM addr += off; 2120*8212SMichael.Corcoran@Sun.COM size = hdr->a_data; 2121*8212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, off, as, &addr, size, PROT_ALL, PROT_ALL, 2122*8212SMichael.Corcoran@Sun.COM mflag, fcred, NULL); 2123*8212SMichael.Corcoran@Sun.COM if (error) { 2124*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 2125*8212SMichael.Corcoran@Sun.COM return (error); 2126*8212SMichael.Corcoran@Sun.COM } 2127*8212SMichael.Corcoran@Sun.COM msize += size; 2128*8212SMichael.Corcoran@Sun.COM mrp[1].mr_addr = addr; 2129*8212SMichael.Corcoran@Sun.COM mrp[1].mr_msize = size; 2130*8212SMichael.Corcoran@Sun.COM mrp[1].mr_fsize = size; 2131*8212SMichael.Corcoran@Sun.COM mrp[1].mr_offset = 0; 2132*8212SMichael.Corcoran@Sun.COM mrp[1].mr_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 2133*8212SMichael.Corcoran@Sun.COM mrp[1].mr_flags = 0; 2134*8212SMichael.Corcoran@Sun.COM 2135*8212SMichael.Corcoran@Sun.COM /* Need to zero out remainder of page */ 2136*8212SMichael.Corcoran@Sun.COM addr += hdr->a_data; 2137*8212SMichael.Corcoran@Sun.COM zfoddiff = P2PHASE((size_t)addr, PAGESIZE); 2138*8212SMichael.Corcoran@Sun.COM if (zfoddiff) { 2139*8212SMichael.Corcoran@Sun.COM label_t ljb; 2140*8212SMichael.Corcoran@Sun.COM 2141*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_zfoddiff); 2142*8212SMichael.Corcoran@Sun.COM zfoddiff = PAGESIZE - zfoddiff; 2143*8212SMichael.Corcoran@Sun.COM if (on_fault(&ljb)) { 2144*8212SMichael.Corcoran@Sun.COM no_fault(); 2145*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_uzero_fault); 2146*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 2147*8212SMichael.Corcoran@Sun.COM return (EFAULT); 2148*8212SMichael.Corcoran@Sun.COM } 2149*8212SMichael.Corcoran@Sun.COM uzero(addr, zfoddiff); 2150*8212SMichael.Corcoran@Sun.COM no_fault(); 2151*8212SMichael.Corcoran@Sun.COM } 2152*8212SMichael.Corcoran@Sun.COM msize += zfoddiff; 2153*8212SMichael.Corcoran@Sun.COM segnum = 2; 2154*8212SMichael.Corcoran@Sun.COM 2155*8212SMichael.Corcoran@Sun.COM /* Map bss */ 2156*8212SMichael.Corcoran@Sun.COM if (hdr->a_bss > zfoddiff) { 2157*8212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = 2158*8212SMichael.Corcoran@Sun.COM SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 2159*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_map_bss); 2160*8212SMichael.Corcoran@Sun.COM addr += zfoddiff; 2161*8212SMichael.Corcoran@Sun.COM size = hdr->a_bss - zfoddiff; 2162*8212SMichael.Corcoran@Sun.COM as_rangelock(as); 2163*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 2164*8212SMichael.Corcoran@Sun.COM error = as_map(as, addr, size, segvn_create, &crargs); 2165*8212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 2166*8212SMichael.Corcoran@Sun.COM msize += size; 2167*8212SMichael.Corcoran@Sun.COM 2168*8212SMichael.Corcoran@Sun.COM if (error) { 2169*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_bss_fail); 2170*8212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 2171*8212SMichael.Corcoran@Sun.COM return (error); 2172*8212SMichael.Corcoran@Sun.COM } 2173*8212SMichael.Corcoran@Sun.COM mrp[2].mr_addr = addr; 2174*8212SMichael.Corcoran@Sun.COM mrp[2].mr_msize = size; 2175*8212SMichael.Corcoran@Sun.COM mrp[2].mr_fsize = 0; 2176*8212SMichael.Corcoran@Sun.COM mrp[2].mr_offset = 0; 2177*8212SMichael.Corcoran@Sun.COM mrp[2].mr_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 2178*8212SMichael.Corcoran@Sun.COM mrp[2].mr_flags = 0; 2179*8212SMichael.Corcoran@Sun.COM 2180*8212SMichael.Corcoran@Sun.COM addr += size; 2181*8212SMichael.Corcoran@Sun.COM segnum = 3; 2182*8212SMichael.Corcoran@Sun.COM } 2183*8212SMichael.Corcoran@Sun.COM 2184*8212SMichael.Corcoran@Sun.COM /* 2185*8212SMichael.Corcoran@Sun.COM * If we have extra bits left over, we need to include that in how 2186*8212SMichael.Corcoran@Sun.COM * much we mapped to make sure the nlist logic is correct 2187*8212SMichael.Corcoran@Sun.COM */ 2188*8212SMichael.Corcoran@Sun.COM msize = P2ROUNDUP(msize, PAGESIZE); 2189*8212SMichael.Corcoran@Sun.COM 2190*8212SMichael.Corcoran@Sun.COM if (nsize && msize < nsize) { 2191*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_nlist); 2192*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_addr = addr; 2193*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_msize = nsize - msize; 2194*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_fsize = 0; 2195*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_offset = 0; 2196*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_prot = PROT_READ | PROT_EXEC; 2197*8212SMichael.Corcoran@Sun.COM mrp[segnum].mr_flags = 0; 2198*8212SMichael.Corcoran@Sun.COM } 2199*8212SMichael.Corcoran@Sun.COM 2200*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 2201*8212SMichael.Corcoran@Sun.COM return (0); 2202*8212SMichael.Corcoran@Sun.COM } 2203*8212SMichael.Corcoran@Sun.COM #endif 2204*8212SMichael.Corcoran@Sun.COM 2205*8212SMichael.Corcoran@Sun.COM /* 2206*8212SMichael.Corcoran@Sun.COM * These are the two types of files that we can interpret and we want to read 2207*8212SMichael.Corcoran@Sun.COM * in enough info to cover both types when looking at the initial header. 2208*8212SMichael.Corcoran@Sun.COM */ 2209*8212SMichael.Corcoran@Sun.COM #define MAX_HEADER_SIZE (MAX(sizeof (Ehdr), sizeof (struct exec))) 2210*8212SMichael.Corcoran@Sun.COM 2211*8212SMichael.Corcoran@Sun.COM /* 2212*8212SMichael.Corcoran@Sun.COM * Map vp passed in in an interpreted manner. ELF and AOUT files will be 2213*8212SMichael.Corcoran@Sun.COM * interpreted and mapped appropriately for execution. 2214*8212SMichael.Corcoran@Sun.COM * num_mapped in - # elements in mrp 2215*8212SMichael.Corcoran@Sun.COM * num_mapped out - # sections mapped and length of mrp array if 2216*8212SMichael.Corcoran@Sun.COM * no errors or E2BIG returned. 2217*8212SMichael.Corcoran@Sun.COM * 2218*8212SMichael.Corcoran@Sun.COM * Returns 0 on success, errno value on failure. 2219*8212SMichael.Corcoran@Sun.COM */ 2220*8212SMichael.Corcoran@Sun.COM static int 2221*8212SMichael.Corcoran@Sun.COM mmapobj_map_interpret(vnode_t *vp, mmapobj_result_t *mrp, 2222*8212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 2223*8212SMichael.Corcoran@Sun.COM { 2224*8212SMichael.Corcoran@Sun.COM int error = 0; 2225*8212SMichael.Corcoran@Sun.COM vattr_t vattr; 2226*8212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 2227*8212SMichael.Corcoran@Sun.COM caddr_t start_addr; 2228*8212SMichael.Corcoran@Sun.COM model_t model; 2229*8212SMichael.Corcoran@Sun.COM 2230*8212SMichael.Corcoran@Sun.COM /* 2231*8212SMichael.Corcoran@Sun.COM * header has to be aligned to the native size of ulong_t in order 2232*8212SMichael.Corcoran@Sun.COM * to avoid an unaligned access when dereferencing the header as 2233*8212SMichael.Corcoran@Sun.COM * a ulong_t. Thus we allocate our array on the stack of type 2234*8212SMichael.Corcoran@Sun.COM * ulong_t and then have header, which we dereference later as a char 2235*8212SMichael.Corcoran@Sun.COM * array point at lheader. 2236*8212SMichael.Corcoran@Sun.COM */ 2237*8212SMichael.Corcoran@Sun.COM ulong_t lheader[(MAX_HEADER_SIZE / (sizeof (ulong_t))) + 1]; 2238*8212SMichael.Corcoran@Sun.COM caddr_t header = (caddr_t)&lheader; 2239*8212SMichael.Corcoran@Sun.COM 2240*8212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_FSID | AT_NODEID | AT_CTIME | AT_MTIME | AT_SIZE; 2241*8212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 2242*8212SMichael.Corcoran@Sun.COM if (error) { 2243*8212SMichael.Corcoran@Sun.COM return (error); 2244*8212SMichael.Corcoran@Sun.COM } 2245*8212SMichael.Corcoran@Sun.COM 2246*8212SMichael.Corcoran@Sun.COM /* 2247*8212SMichael.Corcoran@Sun.COM * Check lib_va to see if we already have a full description 2248*8212SMichael.Corcoran@Sun.COM * for this library. This is the fast path and only used for 2249*8212SMichael.Corcoran@Sun.COM * ET_DYN ELF files (dynamic libraries). 2250*8212SMichael.Corcoran@Sun.COM */ 2251*8212SMichael.Corcoran@Sun.COM if (padding == 0 && (lvp = lib_va_find(&vattr)) != NULL) { 2252*8212SMichael.Corcoran@Sun.COM int num_segs; 2253*8212SMichael.Corcoran@Sun.COM 2254*8212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 2255*8212SMichael.Corcoran@Sun.COM if ((model == DATAMODEL_ILP32 && 2256*8212SMichael.Corcoran@Sun.COM lvp->lv_flags & LV_ELF64) || 2257*8212SMichael.Corcoran@Sun.COM (model == DATAMODEL_LP64 && 2258*8212SMichael.Corcoran@Sun.COM lvp->lv_flags & LV_ELF32)) { 2259*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 2260*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_wrong_model); 2261*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 2262*8212SMichael.Corcoran@Sun.COM } 2263*8212SMichael.Corcoran@Sun.COM num_segs = lvp->lv_num_segs; 2264*8212SMichael.Corcoran@Sun.COM if (*num_mapped < num_segs) { 2265*8212SMichael.Corcoran@Sun.COM *num_mapped = num_segs; 2266*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 2267*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_e2big); 2268*8212SMichael.Corcoran@Sun.COM return (E2BIG); 2269*8212SMichael.Corcoran@Sun.COM } 2270*8212SMichael.Corcoran@Sun.COM 2271*8212SMichael.Corcoran@Sun.COM /* 2272*8212SMichael.Corcoran@Sun.COM * Check to see if we have all the mappable program headers 2273*8212SMichael.Corcoran@Sun.COM * cached. 2274*8212SMichael.Corcoran@Sun.COM */ 2275*8212SMichael.Corcoran@Sun.COM if (num_segs <= LIBVA_CACHED_SEGS && num_segs != 0) { 2276*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast); 2277*8212SMichael.Corcoran@Sun.COM start_addr = mmapobj_lookup_start_addr(lvp); 2278*8212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 2279*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 2280*8212SMichael.Corcoran@Sun.COM return (ENOMEM); 2281*8212SMichael.Corcoran@Sun.COM } 2282*8212SMichael.Corcoran@Sun.COM 2283*8212SMichael.Corcoran@Sun.COM bcopy(lvp->lv_mps, mrp, 2284*8212SMichael.Corcoran@Sun.COM num_segs * sizeof (mmapobj_result_t)); 2285*8212SMichael.Corcoran@Sun.COM 2286*8212SMichael.Corcoran@Sun.COM error = mmapobj_map_elf(vp, start_addr, mrp, 2287*8212SMichael.Corcoran@Sun.COM num_segs, fcred, ET_DYN); 2288*8212SMichael.Corcoran@Sun.COM 2289*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 2290*8212SMichael.Corcoran@Sun.COM if (error == 0) { 2291*8212SMichael.Corcoran@Sun.COM *num_mapped = num_segs; 2292*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_success); 2293*8212SMichael.Corcoran@Sun.COM } 2294*8212SMichael.Corcoran@Sun.COM return (error); 2295*8212SMichael.Corcoran@Sun.COM } 2296*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_not_now); 2297*8212SMichael.Corcoran@Sun.COM 2298*8212SMichael.Corcoran@Sun.COM /* Release it for now since we'll look it up below */ 2299*8212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 2300*8212SMichael.Corcoran@Sun.COM } 2301*8212SMichael.Corcoran@Sun.COM 2302*8212SMichael.Corcoran@Sun.COM /* 2303*8212SMichael.Corcoran@Sun.COM * Time to see if this is a file we can interpret. If it's smaller 2304*8212SMichael.Corcoran@Sun.COM * than this, then we can't interpret it. 2305*8212SMichael.Corcoran@Sun.COM */ 2306*8212SMichael.Corcoran@Sun.COM if (vattr.va_size < MAX_HEADER_SIZE) { 2307*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(small_file); 2308*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 2309*8212SMichael.Corcoran@Sun.COM } 2310*8212SMichael.Corcoran@Sun.COM 2311*8212SMichael.Corcoran@Sun.COM if ((error = vn_rdwr(UIO_READ, vp, header, MAX_HEADER_SIZE, 0, 2312*8212SMichael.Corcoran@Sun.COM UIO_SYSSPACE, 0, (rlim64_t)0, fcred, NULL)) != 0) { 2313*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(read_error); 2314*8212SMichael.Corcoran@Sun.COM return (error); 2315*8212SMichael.Corcoran@Sun.COM } 2316*8212SMichael.Corcoran@Sun.COM 2317*8212SMichael.Corcoran@Sun.COM /* Verify file type */ 2318*8212SMichael.Corcoran@Sun.COM if (header[EI_MAG0] == ELFMAG0 && header[EI_MAG1] == ELFMAG1 && 2319*8212SMichael.Corcoran@Sun.COM header[EI_MAG2] == ELFMAG2 && header[EI_MAG3] == ELFMAG3) { 2320*8212SMichael.Corcoran@Sun.COM return (doelfwork((Ehdr *)lheader, vp, mrp, num_mapped, 2321*8212SMichael.Corcoran@Sun.COM padding, fcred)); 2322*8212SMichael.Corcoran@Sun.COM } 2323*8212SMichael.Corcoran@Sun.COM 2324*8212SMichael.Corcoran@Sun.COM #if defined(__sparc) 2325*8212SMichael.Corcoran@Sun.COM /* On sparc, check for 4.X AOUT format */ 2326*8212SMichael.Corcoran@Sun.COM switch (((struct exec *)header)->a_magic) { 2327*8212SMichael.Corcoran@Sun.COM case OMAGIC: 2328*8212SMichael.Corcoran@Sun.COM case ZMAGIC: 2329*8212SMichael.Corcoran@Sun.COM case NMAGIC: 2330*8212SMichael.Corcoran@Sun.COM return (doaoutwork(vp, mrp, num_mapped, 2331*8212SMichael.Corcoran@Sun.COM (struct exec *)lheader, fcred)); 2332*8212SMichael.Corcoran@Sun.COM } 2333*8212SMichael.Corcoran@Sun.COM #endif 2334*8212SMichael.Corcoran@Sun.COM 2335*8212SMichael.Corcoran@Sun.COM /* Unsupported type */ 2336*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unsupported); 2337*8212SMichael.Corcoran@Sun.COM return (ENOTSUP); 2338*8212SMichael.Corcoran@Sun.COM } 2339*8212SMichael.Corcoran@Sun.COM 2340*8212SMichael.Corcoran@Sun.COM /* 2341*8212SMichael.Corcoran@Sun.COM * Given a vnode, map it as either a flat file or interpret it and map 2342*8212SMichael.Corcoran@Sun.COM * it according to the rules of the file type. 2343*8212SMichael.Corcoran@Sun.COM * *num_mapped will contain the size of the mmapobj_result_t array passed in. 2344*8212SMichael.Corcoran@Sun.COM * If padding is non-zero, the mappings will be padded by that amount 2345*8212SMichael.Corcoran@Sun.COM * rounded up to the nearest pagesize. 2346*8212SMichael.Corcoran@Sun.COM * If the mapping is successful, *num_mapped will contain the number of 2347*8212SMichael.Corcoran@Sun.COM * distinct mappings created, and mrp will point to the array of 2348*8212SMichael.Corcoran@Sun.COM * mmapobj_result_t's which describe these mappings. 2349*8212SMichael.Corcoran@Sun.COM * 2350*8212SMichael.Corcoran@Sun.COM * On error, -1 is returned and errno is set appropriately. 2351*8212SMichael.Corcoran@Sun.COM * A special error case will set errno to E2BIG when there are more than 2352*8212SMichael.Corcoran@Sun.COM * *num_mapped mappings to be created and *num_mapped will be set to the 2353*8212SMichael.Corcoran@Sun.COM * number of mappings needed. 2354*8212SMichael.Corcoran@Sun.COM */ 2355*8212SMichael.Corcoran@Sun.COM int 2356*8212SMichael.Corcoran@Sun.COM mmapobj(vnode_t *vp, uint_t flags, mmapobj_result_t *mrp, 2357*8212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 2358*8212SMichael.Corcoran@Sun.COM { 2359*8212SMichael.Corcoran@Sun.COM int to_map; 2360*8212SMichael.Corcoran@Sun.COM int error = 0; 2361*8212SMichael.Corcoran@Sun.COM 2362*8212SMichael.Corcoran@Sun.COM ASSERT((padding & PAGEOFFSET) == 0); 2363*8212SMichael.Corcoran@Sun.COM ASSERT((flags & ~MMOBJ_ALL_FLAGS) == 0); 2364*8212SMichael.Corcoran@Sun.COM ASSERT(num_mapped != NULL); 2365*8212SMichael.Corcoran@Sun.COM ASSERT((flags & MMOBJ_PADDING) ? padding != 0 : padding == 0); 2366*8212SMichael.Corcoran@Sun.COM 2367*8212SMichael.Corcoran@Sun.COM if ((flags & MMOBJ_INTERPRET) == 0) { 2368*8212SMichael.Corcoran@Sun.COM to_map = padding ? 3 : 1; 2369*8212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 2370*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 2371*8212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(flat_e2big); 2372*8212SMichael.Corcoran@Sun.COM return (E2BIG); 2373*8212SMichael.Corcoran@Sun.COM } 2374*8212SMichael.Corcoran@Sun.COM error = mmapobj_map_flat(vp, mrp, padding, fcred); 2375*8212SMichael.Corcoran@Sun.COM 2376*8212SMichael.Corcoran@Sun.COM if (error) { 2377*8212SMichael.Corcoran@Sun.COM return (error); 2378*8212SMichael.Corcoran@Sun.COM } 2379*8212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 2380*8212SMichael.Corcoran@Sun.COM return (0); 2381*8212SMichael.Corcoran@Sun.COM } 2382*8212SMichael.Corcoran@Sun.COM 2383*8212SMichael.Corcoran@Sun.COM error = mmapobj_map_interpret(vp, mrp, num_mapped, padding, fcred); 2384*8212SMichael.Corcoran@Sun.COM return (error); 2385*8212SMichael.Corcoran@Sun.COM } 2386