18212SMichael.Corcoran@Sun.COM /* 28212SMichael.Corcoran@Sun.COM * CDDL HEADER START 38212SMichael.Corcoran@Sun.COM * 48212SMichael.Corcoran@Sun.COM * The contents of this file are subject to the terms of the 58212SMichael.Corcoran@Sun.COM * Common Development and Distribution License (the "License"). 68212SMichael.Corcoran@Sun.COM * You may not use this file except in compliance with the License. 78212SMichael.Corcoran@Sun.COM * 88212SMichael.Corcoran@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98212SMichael.Corcoran@Sun.COM * or http://www.opensolaris.org/os/licensing. 108212SMichael.Corcoran@Sun.COM * See the License for the specific language governing permissions 118212SMichael.Corcoran@Sun.COM * and limitations under the License. 128212SMichael.Corcoran@Sun.COM * 138212SMichael.Corcoran@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148212SMichael.Corcoran@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158212SMichael.Corcoran@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168212SMichael.Corcoran@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178212SMichael.Corcoran@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188212SMichael.Corcoran@Sun.COM * 198212SMichael.Corcoran@Sun.COM * CDDL HEADER END 208212SMichael.Corcoran@Sun.COM */ 218212SMichael.Corcoran@Sun.COM /* 228212SMichael.Corcoran@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 238212SMichael.Corcoran@Sun.COM * Use is subject to license terms. 248212SMichael.Corcoran@Sun.COM */ 258212SMichael.Corcoran@Sun.COM 268212SMichael.Corcoran@Sun.COM #include <sys/types.h> 278212SMichael.Corcoran@Sun.COM #include <sys/sysmacros.h> 288212SMichael.Corcoran@Sun.COM #include <sys/kmem.h> 298212SMichael.Corcoran@Sun.COM #include <sys/param.h> 308212SMichael.Corcoran@Sun.COM #include <sys/systm.h> 318212SMichael.Corcoran@Sun.COM #include <sys/errno.h> 328212SMichael.Corcoran@Sun.COM #include <sys/mman.h> 338212SMichael.Corcoran@Sun.COM #include <sys/cmn_err.h> 348212SMichael.Corcoran@Sun.COM #include <sys/cred.h> 358212SMichael.Corcoran@Sun.COM #include <sys/vmsystm.h> 368212SMichael.Corcoran@Sun.COM #include <sys/debug.h> 378212SMichael.Corcoran@Sun.COM #include <vm/as.h> 388212SMichael.Corcoran@Sun.COM #include <vm/seg.h> 398212SMichael.Corcoran@Sun.COM #include <sys/vmparam.h> 408212SMichael.Corcoran@Sun.COM #include <sys/vfs.h> 418212SMichael.Corcoran@Sun.COM #include <sys/elf.h> 428212SMichael.Corcoran@Sun.COM #include <sys/machelf.h> 438212SMichael.Corcoran@Sun.COM #include <sys/corectl.h> 448212SMichael.Corcoran@Sun.COM #include <sys/exec.h> 458212SMichael.Corcoran@Sun.COM #include <sys/exechdr.h> 468212SMichael.Corcoran@Sun.COM #include <sys/autoconf.h> 478212SMichael.Corcoran@Sun.COM #include <sys/mem.h> 488212SMichael.Corcoran@Sun.COM #include <vm/seg_dev.h> 498212SMichael.Corcoran@Sun.COM #include <sys/vmparam.h> 508212SMichael.Corcoran@Sun.COM #include <sys/mmapobj.h> 518212SMichael.Corcoran@Sun.COM #include <sys/atomic.h> 528212SMichael.Corcoran@Sun.COM 538212SMichael.Corcoran@Sun.COM /* 548212SMichael.Corcoran@Sun.COM * Theory statement: 558212SMichael.Corcoran@Sun.COM * 568212SMichael.Corcoran@Sun.COM * The main driving force behind mmapobj is to interpret and map ELF files 578212SMichael.Corcoran@Sun.COM * inside of the kernel instead of having the linker be responsible for this. 588212SMichael.Corcoran@Sun.COM * 598212SMichael.Corcoran@Sun.COM * mmapobj also supports the AOUT 4.x binary format as well as flat files in 608212SMichael.Corcoran@Sun.COM * a read only manner. 618212SMichael.Corcoran@Sun.COM * 628212SMichael.Corcoran@Sun.COM * When interpreting and mapping an ELF file, mmapobj will map each PT_LOAD 638212SMichael.Corcoran@Sun.COM * or PT_SUNWBSS segment according to the ELF standard. Refer to the "Linker 648212SMichael.Corcoran@Sun.COM * and Libraries Guide" for more information about the standard and mapping 658212SMichael.Corcoran@Sun.COM * rules. 668212SMichael.Corcoran@Sun.COM * 678212SMichael.Corcoran@Sun.COM * Having mmapobj interpret and map objects will allow the kernel to make the 688212SMichael.Corcoran@Sun.COM * best decision for where to place the mappings for said objects. Thus, we 698212SMichael.Corcoran@Sun.COM * can make optimizations inside of the kernel for specific platforms or 708212SMichael.Corcoran@Sun.COM * cache mapping information to make mapping objects faster. 718212SMichael.Corcoran@Sun.COM * 728212SMichael.Corcoran@Sun.COM * The lib_va_hash will be one such optimization. For each ELF object that 738212SMichael.Corcoran@Sun.COM * mmapobj is asked to interpret, we will attempt to cache the information 748212SMichael.Corcoran@Sun.COM * about the PT_LOAD and PT_SUNWBSS sections to speed up future mappings of 758212SMichael.Corcoran@Sun.COM * the same objects. We will cache up to LIBVA_CACHED_SEGS (see below) program 768212SMichael.Corcoran@Sun.COM * headers which should cover a majority of the libraries out there without 778212SMichael.Corcoran@Sun.COM * wasting space. In order to make sure that the cached information is valid, 788212SMichael.Corcoran@Sun.COM * we check the passed in vnode's mtime and ctime to make sure the vnode 798212SMichael.Corcoran@Sun.COM * has not been modified since the last time we used it. 808212SMichael.Corcoran@Sun.COM * 818212SMichael.Corcoran@Sun.COM * In addition, the lib_va_hash may contain a preferred starting VA for the 828212SMichael.Corcoran@Sun.COM * object which can be useful for platforms which support a shared context. 838212SMichael.Corcoran@Sun.COM * This will increase the likelyhood that library text can be shared among 848212SMichael.Corcoran@Sun.COM * many different processes. We limit the reserved VA space for 32 bit objects 858212SMichael.Corcoran@Sun.COM * in order to minimize fragmenting the processes address space. 868212SMichael.Corcoran@Sun.COM * 878212SMichael.Corcoran@Sun.COM * In addition to the above, the mmapobj interface allows for padding to be 888212SMichael.Corcoran@Sun.COM * requested before the first mapping and after the last mapping created. 898212SMichael.Corcoran@Sun.COM * When padding is requested, no additional optimizations will be made for 908212SMichael.Corcoran@Sun.COM * that request. 918212SMichael.Corcoran@Sun.COM */ 928212SMichael.Corcoran@Sun.COM 938212SMichael.Corcoran@Sun.COM /* 948212SMichael.Corcoran@Sun.COM * Threshold to prevent allocating too much kernel memory to read in the 958212SMichael.Corcoran@Sun.COM * program headers for an object. If it requires more than below, 968212SMichael.Corcoran@Sun.COM * we will use a KM_NOSLEEP allocation to allocate memory to hold all of the 978212SMichael.Corcoran@Sun.COM * program headers which could possibly fail. If less memory than below is 988212SMichael.Corcoran@Sun.COM * needed, then we use a KM_SLEEP allocation and are willing to wait for the 998212SMichael.Corcoran@Sun.COM * memory if we need to. 1008212SMichael.Corcoran@Sun.COM */ 1018212SMichael.Corcoran@Sun.COM size_t mmapobj_alloc_threshold = 65536; 1028212SMichael.Corcoran@Sun.COM 1038212SMichael.Corcoran@Sun.COM /* Debug stats for test coverage */ 1048212SMichael.Corcoran@Sun.COM #ifdef DEBUG 1058212SMichael.Corcoran@Sun.COM struct mobj_stats { 1068212SMichael.Corcoran@Sun.COM uint_t mobjs_unmap_called; 1078212SMichael.Corcoran@Sun.COM uint_t mobjs_remap_devnull; 1088212SMichael.Corcoran@Sun.COM uint_t mobjs_lookup_start; 1098212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_start; 1108212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_vmem; 1118212SMichael.Corcoran@Sun.COM uint_t mobjs_add_collision; 1128212SMichael.Corcoran@Sun.COM uint_t mobjs_get_addr; 1138212SMichael.Corcoran@Sun.COM uint_t mobjs_map_flat_no_padding; 1148212SMichael.Corcoran@Sun.COM uint_t mobjs_map_flat_padding; 1158212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_text; 1168212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_initdata; 1178212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_preread; 1188212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_text; 1198212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_map_fail; 1208212SMichael.Corcoran@Sun.COM uint_t mobjs_map_ptload_unaligned_read_fail; 1218212SMichael.Corcoran@Sun.COM uint_t mobjs_zfoddiff; 1228212SMichael.Corcoran@Sun.COM uint_t mobjs_zfoddiff_nowrite; 1238212SMichael.Corcoran@Sun.COM uint_t mobjs_zfodextra; 1248212SMichael.Corcoran@Sun.COM uint_t mobjs_ptload_failed; 1258212SMichael.Corcoran@Sun.COM uint_t mobjs_map_elf_no_holes; 1268212SMichael.Corcoran@Sun.COM uint_t mobjs_unmap_hole; 1278212SMichael.Corcoran@Sun.COM uint_t mobjs_nomem_header; 1288212SMichael.Corcoran@Sun.COM uint_t mobjs_overlap_header; 1298212SMichael.Corcoran@Sun.COM uint_t mobjs_np2_align; 1308212SMichael.Corcoran@Sun.COM uint_t mobjs_np2_align_overflow; 1318212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_padding; 1328212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_mapped; 1338212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_devnull; 1348212SMichael.Corcoran@Sun.COM uint_t mobjs_exec_addr_in_use; 1358212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_found; 1368212SMichael.Corcoran@Sun.COM uint_t mobjs_no_loadable_yet; 1378212SMichael.Corcoran@Sun.COM uint_t mobjs_nothing_to_map; 1388212SMichael.Corcoran@Sun.COM uint_t mobjs_e2big; 1398212SMichael.Corcoran@Sun.COM uint_t mobjs_dyn_pad_align; 1408212SMichael.Corcoran@Sun.COM uint_t mobjs_dyn_pad_noalign; 1418212SMichael.Corcoran@Sun.COM uint_t mobjs_alloc_start_fail; 1428212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_nocache; 1438212SMichael.Corcoran@Sun.COM uint_t mobjs_extra_padding; 1448212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_not_needed; 1458212SMichael.Corcoran@Sun.COM uint_t mobjs_no_mem_map_sz; 1468212SMichael.Corcoran@Sun.COM uint_t mobjs_check_exec_failed; 1478212SMichael.Corcoran@Sun.COM uint_t mobjs_lvp_used; 1488212SMichael.Corcoran@Sun.COM uint_t mobjs_wrong_model; 1498212SMichael.Corcoran@Sun.COM uint_t mobjs_noexec_fs; 1508212SMichael.Corcoran@Sun.COM uint_t mobjs_e2big_et_rel; 1518212SMichael.Corcoran@Sun.COM uint_t mobjs_et_rel_mapped; 1528212SMichael.Corcoran@Sun.COM uint_t mobjs_unknown_elf_type; 1538212SMichael.Corcoran@Sun.COM uint_t mobjs_phent32_too_small; 1548212SMichael.Corcoran@Sun.COM uint_t mobjs_phent64_too_small; 1558212SMichael.Corcoran@Sun.COM uint_t mobjs_inval_elf_class; 1568212SMichael.Corcoran@Sun.COM uint_t mobjs_too_many_phdrs; 1578212SMichael.Corcoran@Sun.COM uint_t mobjs_no_phsize; 1588212SMichael.Corcoran@Sun.COM uint_t mobjs_phsize_large; 1598212SMichael.Corcoran@Sun.COM uint_t mobjs_phsize_xtralarge; 1608212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_wrong_model; 1618212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_e2big; 1628212SMichael.Corcoran@Sun.COM uint_t mobjs_fast; 1638212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_success; 1648212SMichael.Corcoran@Sun.COM uint_t mobjs_fast_not_now; 1658212SMichael.Corcoran@Sun.COM uint_t mobjs_small_file; 1668212SMichael.Corcoran@Sun.COM uint_t mobjs_read_error; 1678212SMichael.Corcoran@Sun.COM uint_t mobjs_unsupported; 1688212SMichael.Corcoran@Sun.COM uint_t mobjs_flat_e2big; 1698212SMichael.Corcoran@Sun.COM uint_t mobjs_phent_align32; 1708212SMichael.Corcoran@Sun.COM uint_t mobjs_phent_align64; 1718212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_hit; 1728212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_delay_delete; 1738212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_find_delete; 1748212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_add_delay_delete; 1758212SMichael.Corcoran@Sun.COM uint_t mobjs_lib_va_add_delete; 176*8362SMichael.Corcoran@Sun.COM uint_t mobjs_min_align; 1778212SMichael.Corcoran@Sun.COM #if defined(__sparc) 1788212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_uzero_fault; 1798212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_64bit_try; 1808212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_noexec; 1818212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_e2big; 1828212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_lib; 1838212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_fixed; 1848212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_zfoddiff; 1858212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_map_bss; 1868212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_bss_fail; 1878212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_nlist; 1888212SMichael.Corcoran@Sun.COM uint_t mobjs_aout_addr_in_use; 1898212SMichael.Corcoran@Sun.COM #endif 1908212SMichael.Corcoran@Sun.COM } mobj_stats; 1918212SMichael.Corcoran@Sun.COM 1928212SMichael.Corcoran@Sun.COM #define MOBJ_STAT_ADD(stat) ((mobj_stats.mobjs_##stat)++) 1938212SMichael.Corcoran@Sun.COM #else 1948212SMichael.Corcoran@Sun.COM #define MOBJ_STAT_ADD(stat) 1958212SMichael.Corcoran@Sun.COM #endif 1968212SMichael.Corcoran@Sun.COM 1978212SMichael.Corcoran@Sun.COM /* lv_flags values - bitmap */ 1988212SMichael.Corcoran@Sun.COM #define LV_ELF32 0x1 /* 32 bit ELF file */ 1998212SMichael.Corcoran@Sun.COM #define LV_ELF64 0x2 /* 64 bit ELF file */ 2008212SMichael.Corcoran@Sun.COM #define LV_DEL 0x4 /* delete when lv_refcnt hits zero */ 2018212SMichael.Corcoran@Sun.COM 2028212SMichael.Corcoran@Sun.COM /* 2038212SMichael.Corcoran@Sun.COM * Note: lv_num_segs will denote how many segments this file has and will 2048212SMichael.Corcoran@Sun.COM * only be set after the lv_mps array has been filled out. 2058212SMichael.Corcoran@Sun.COM * lv_mps can only be valid if lv_num_segs is non-zero. 2068212SMichael.Corcoran@Sun.COM */ 2078212SMichael.Corcoran@Sun.COM struct lib_va { 2088212SMichael.Corcoran@Sun.COM struct lib_va *lv_next; 2098212SMichael.Corcoran@Sun.COM caddr_t lv_base_va; /* start va for library */ 2108212SMichael.Corcoran@Sun.COM ssize_t lv_len; /* total va span of library */ 2118212SMichael.Corcoran@Sun.COM size_t lv_align; /* minimum alignment */ 2128212SMichael.Corcoran@Sun.COM uint64_t lv_nodeid; /* filesystem node id */ 2138212SMichael.Corcoran@Sun.COM uint64_t lv_fsid; /* filesystem id */ 2148212SMichael.Corcoran@Sun.COM timestruc_t lv_ctime; /* last time file was changed */ 2158212SMichael.Corcoran@Sun.COM timestruc_t lv_mtime; /* or modified */ 2168212SMichael.Corcoran@Sun.COM mmapobj_result_t lv_mps[LIBVA_CACHED_SEGS]; /* cached pheaders */ 2178212SMichael.Corcoran@Sun.COM int lv_num_segs; /* # segs for this file */ 2188212SMichael.Corcoran@Sun.COM int lv_flags; 2198212SMichael.Corcoran@Sun.COM uint_t lv_refcnt; /* number of holds on struct */ 2208212SMichael.Corcoran@Sun.COM }; 2218212SMichael.Corcoran@Sun.COM 2228212SMichael.Corcoran@Sun.COM #define LIB_VA_SIZE 1024 2238212SMichael.Corcoran@Sun.COM #define LIB_VA_MASK (LIB_VA_SIZE - 1) 2248212SMichael.Corcoran@Sun.COM #define LIB_VA_MUTEX_SHIFT 3 2258212SMichael.Corcoran@Sun.COM 2268212SMichael.Corcoran@Sun.COM #if (LIB_VA_SIZE & (LIB_VA_SIZE - 1)) 2278212SMichael.Corcoran@Sun.COM #error "LIB_VA_SIZE is not a power of 2" 2288212SMichael.Corcoran@Sun.COM #endif 2298212SMichael.Corcoran@Sun.COM 2308212SMichael.Corcoran@Sun.COM static struct lib_va *lib_va_hash[LIB_VA_SIZE]; 2318212SMichael.Corcoran@Sun.COM static kmutex_t lib_va_hash_mutex[LIB_VA_SIZE >> LIB_VA_MUTEX_SHIFT]; 2328212SMichael.Corcoran@Sun.COM 2338212SMichael.Corcoran@Sun.COM #define LIB_VA_HASH_MUTEX(index) \ 2348212SMichael.Corcoran@Sun.COM (&lib_va_hash_mutex[index >> LIB_VA_MUTEX_SHIFT]) 2358212SMichael.Corcoran@Sun.COM 2368212SMichael.Corcoran@Sun.COM #define LIB_VA_HASH(nodeid) \ 2378212SMichael.Corcoran@Sun.COM (((nodeid) ^ ((nodeid) << 7) ^ ((nodeid) << 13)) & LIB_VA_MASK) 2388212SMichael.Corcoran@Sun.COM 2398212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH_ID(arg1, arg2) \ 2408212SMichael.Corcoran@Sun.COM ((arg1)->lv_nodeid == (arg2)->va_nodeid && \ 2418212SMichael.Corcoran@Sun.COM (arg1)->lv_fsid == (arg2)->va_fsid) 2428212SMichael.Corcoran@Sun.COM 2438212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH_TIME(arg1, arg2) \ 2448212SMichael.Corcoran@Sun.COM ((arg1)->lv_ctime.tv_sec == (arg2)->va_ctime.tv_sec && \ 2458212SMichael.Corcoran@Sun.COM (arg1)->lv_mtime.tv_sec == (arg2)->va_mtime.tv_sec && \ 2468212SMichael.Corcoran@Sun.COM (arg1)->lv_ctime.tv_nsec == (arg2)->va_ctime.tv_nsec && \ 2478212SMichael.Corcoran@Sun.COM (arg1)->lv_mtime.tv_nsec == (arg2)->va_mtime.tv_nsec) 2488212SMichael.Corcoran@Sun.COM 2498212SMichael.Corcoran@Sun.COM #define LIB_VA_MATCH(arg1, arg2) \ 2508212SMichael.Corcoran@Sun.COM (LIB_VA_MATCH_ID(arg1, arg2) && LIB_VA_MATCH_TIME(arg1, arg2)) 2518212SMichael.Corcoran@Sun.COM 2528212SMichael.Corcoran@Sun.COM /* 2538212SMichael.Corcoran@Sun.COM * In order to map libraries at the same VA in many processes, we need to carve 2548212SMichael.Corcoran@Sun.COM * out our own address space for them which is unique across many processes. 2558212SMichael.Corcoran@Sun.COM * We use different arenas for 32 bit and 64 bit libraries. 2568212SMichael.Corcoran@Sun.COM * 2578212SMichael.Corcoran@Sun.COM * Since the 32 bit address space is relatively small, we limit the number of 2588212SMichael.Corcoran@Sun.COM * libraries which try to use consistent virtual addresses to lib_threshold. 2598212SMichael.Corcoran@Sun.COM * For 64 bit libraries there is no such limit since the address space is large. 2608212SMichael.Corcoran@Sun.COM */ 2618212SMichael.Corcoran@Sun.COM static vmem_t *lib_va_32_arena; 2628212SMichael.Corcoran@Sun.COM static vmem_t *lib_va_64_arena; 2638212SMichael.Corcoran@Sun.COM uint_t lib_threshold = 20; /* modifiable via /etc/system */ 2648212SMichael.Corcoran@Sun.COM 2658212SMichael.Corcoran@Sun.COM /* 2668212SMichael.Corcoran@Sun.COM * Number of 32 bit and 64 bit libraries in lib_va hash. 2678212SMichael.Corcoran@Sun.COM */ 2688212SMichael.Corcoran@Sun.COM static uint_t libs_mapped_32 = 0; 2698212SMichael.Corcoran@Sun.COM static uint_t libs_mapped_64 = 0; 2708212SMichael.Corcoran@Sun.COM 2718212SMichael.Corcoran@Sun.COM /* 2728212SMichael.Corcoran@Sun.COM * Initialize the VA span of the lib_va arenas to about half of the VA space 2738212SMichael.Corcoran@Sun.COM * of a user process. These VAs will be used for optimized allocation of 2748212SMichael.Corcoran@Sun.COM * libraries, such that subsequent mappings of the same library will attempt 2758212SMichael.Corcoran@Sun.COM * to use the same VA as previous mappings of that library. 2768212SMichael.Corcoran@Sun.COM */ 2778212SMichael.Corcoran@Sun.COM void 2788212SMichael.Corcoran@Sun.COM lib_va_init(void) 2798212SMichael.Corcoran@Sun.COM { 2808212SMichael.Corcoran@Sun.COM size_t start; 2818212SMichael.Corcoran@Sun.COM size_t end; 2828212SMichael.Corcoran@Sun.COM size_t len; 2838212SMichael.Corcoran@Sun.COM /* 2848212SMichael.Corcoran@Sun.COM * On 32 bit sparc, the user stack and /lib/ld.so.1 will both live 2858212SMichael.Corcoran@Sun.COM * above the end address that we choose. On 32bit x86 only 2868212SMichael.Corcoran@Sun.COM * /lib/ld.so.1 will live above the end address that we choose 2878212SMichael.Corcoran@Sun.COM * because the user stack is at the bottom of the address space. 2888212SMichael.Corcoran@Sun.COM * 2898212SMichael.Corcoran@Sun.COM * We estimate the size of ld.so.1 to be 512K which leaves significant 2908212SMichael.Corcoran@Sun.COM * room for growth without needing to change this value. Thus it is 2918212SMichael.Corcoran@Sun.COM * safe for libraries to be mapped up to that address. 2928212SMichael.Corcoran@Sun.COM * 2938212SMichael.Corcoran@Sun.COM * If the length of ld.so.1 were to grow beyond 512K then 2948212SMichael.Corcoran@Sun.COM * a library who has a reserved address in that range would always 2958212SMichael.Corcoran@Sun.COM * fail to get that address and would have to call map_addr 2968212SMichael.Corcoran@Sun.COM * to get an unused address range. On DEBUG kernels, we will check 2978212SMichael.Corcoran@Sun.COM * on the first use of lib_va that our address does not overlap 2988212SMichael.Corcoran@Sun.COM * ld.so.1, and if it does, then we'll print a cmn_err message. 2998212SMichael.Corcoran@Sun.COM */ 3008212SMichael.Corcoran@Sun.COM #if defined(__sparc) 3018212SMichael.Corcoran@Sun.COM end = _userlimit32 - DFLSSIZ - (512 * 1024); 3028212SMichael.Corcoran@Sun.COM #elif defined(__i386) || defined(__amd64) 3038212SMichael.Corcoran@Sun.COM end = _userlimit32 - (512 * 1024); 3048212SMichael.Corcoran@Sun.COM #else 3058212SMichael.Corcoran@Sun.COM #error "no recognized machine type is defined" 3068212SMichael.Corcoran@Sun.COM #endif 3078212SMichael.Corcoran@Sun.COM len = end >> 1; 3088212SMichael.Corcoran@Sun.COM len = P2ROUNDUP(len, PAGESIZE); 3098212SMichael.Corcoran@Sun.COM start = end - len; 3108212SMichael.Corcoran@Sun.COM lib_va_32_arena = vmem_create("lib_va_32", (void *)start, len, 3118212SMichael.Corcoran@Sun.COM PAGESIZE, NULL, NULL, NULL, 0, VM_NOSLEEP | VMC_IDENTIFIER); 3128212SMichael.Corcoran@Sun.COM 3138212SMichael.Corcoran@Sun.COM #if defined(_LP64) 3148212SMichael.Corcoran@Sun.COM /* 3158212SMichael.Corcoran@Sun.COM * The user stack and /lib/ld.so.1 will both live above the end address 3168212SMichael.Corcoran@Sun.COM * that we choose. We estimate the size of a mapped ld.so.1 to be 2M 3178212SMichael.Corcoran@Sun.COM * which leaves significant room for growth without needing to change 3188212SMichael.Corcoran@Sun.COM * this value. Thus it is safe for libraries to be mapped up to 3198212SMichael.Corcoran@Sun.COM * that address. The same considerations for the size of ld.so.1 that 3208212SMichael.Corcoran@Sun.COM * were mentioned above also apply here. 3218212SMichael.Corcoran@Sun.COM */ 3228212SMichael.Corcoran@Sun.COM end = _userlimit - DFLSSIZ - (2 * 1024 * 1024); 3238212SMichael.Corcoran@Sun.COM len = end >> 1; 3248212SMichael.Corcoran@Sun.COM len = P2ROUNDUP(len, PAGESIZE); 3258212SMichael.Corcoran@Sun.COM start = end - len; 3268212SMichael.Corcoran@Sun.COM lib_va_64_arena = vmem_create("lib_va_64", (void *)start, len, 3278212SMichael.Corcoran@Sun.COM PAGESIZE, NULL, NULL, NULL, 0, VM_NOSLEEP | VMC_IDENTIFIER); 3288212SMichael.Corcoran@Sun.COM #endif 3298212SMichael.Corcoran@Sun.COM } 3308212SMichael.Corcoran@Sun.COM 3318212SMichael.Corcoran@Sun.COM /* 3328212SMichael.Corcoran@Sun.COM * Free up the resources associated with lvp as well as lvp itself. 3338212SMichael.Corcoran@Sun.COM * We also decrement the number of libraries mapped via a lib_va 3348212SMichael.Corcoran@Sun.COM * cached virtual address. 3358212SMichael.Corcoran@Sun.COM */ 3368212SMichael.Corcoran@Sun.COM void 3378212SMichael.Corcoran@Sun.COM lib_va_free(struct lib_va *lvp) 3388212SMichael.Corcoran@Sun.COM { 3398212SMichael.Corcoran@Sun.COM int is_64bit = lvp->lv_flags & LV_ELF64; 3408212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_refcnt == 0); 3418212SMichael.Corcoran@Sun.COM 3428212SMichael.Corcoran@Sun.COM if (lvp->lv_base_va != NULL) { 3438212SMichael.Corcoran@Sun.COM vmem_xfree(is_64bit ? lib_va_64_arena : lib_va_32_arena, 3448212SMichael.Corcoran@Sun.COM lvp->lv_base_va, lvp->lv_len); 3458212SMichael.Corcoran@Sun.COM if (is_64bit) { 3468212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_64, -1); 3478212SMichael.Corcoran@Sun.COM } else { 3488212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_32, -1); 3498212SMichael.Corcoran@Sun.COM } 3508212SMichael.Corcoran@Sun.COM } 3518212SMichael.Corcoran@Sun.COM kmem_free(lvp, sizeof (struct lib_va)); 3528212SMichael.Corcoran@Sun.COM } 3538212SMichael.Corcoran@Sun.COM 3548212SMichael.Corcoran@Sun.COM /* 3558212SMichael.Corcoran@Sun.COM * See if the file associated with the vap passed in is in the lib_va hash. 3568212SMichael.Corcoran@Sun.COM * If it is and the file has not been modified since last use, then 3578212SMichael.Corcoran@Sun.COM * return a pointer to that data. Otherwise, return NULL if the file has 3588212SMichael.Corcoran@Sun.COM * changed or the file was not found in the hash. 3598212SMichael.Corcoran@Sun.COM */ 3608212SMichael.Corcoran@Sun.COM static struct lib_va * 3618212SMichael.Corcoran@Sun.COM lib_va_find(vattr_t *vap) 3628212SMichael.Corcoran@Sun.COM { 3638212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 3648212SMichael.Corcoran@Sun.COM struct lib_va *del = NULL; 3658212SMichael.Corcoran@Sun.COM struct lib_va **tmp; 3668212SMichael.Corcoran@Sun.COM uint_t index; 3678212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(vap->va_nodeid); 3688212SMichael.Corcoran@Sun.COM 3698212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 3708212SMichael.Corcoran@Sun.COM tmp = &lib_va_hash[index]; 3718212SMichael.Corcoran@Sun.COM while (*tmp != NULL) { 3728212SMichael.Corcoran@Sun.COM lvp = *tmp; 3738212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_ID(lvp, vap)) { 3748212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_TIME(lvp, vap)) { 3758212SMichael.Corcoran@Sun.COM ASSERT((lvp->lv_flags & LV_DEL) == 0); 3768212SMichael.Corcoran@Sun.COM lvp->lv_refcnt++; 3778212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_hit); 3788212SMichael.Corcoran@Sun.COM } else { 3798212SMichael.Corcoran@Sun.COM /* 3808212SMichael.Corcoran@Sun.COM * file was updated since last use. 3818212SMichael.Corcoran@Sun.COM * need to remove it from list. 3828212SMichael.Corcoran@Sun.COM */ 3838212SMichael.Corcoran@Sun.COM del = lvp; 3848212SMichael.Corcoran@Sun.COM *tmp = del->lv_next; 3858212SMichael.Corcoran@Sun.COM del->lv_next = NULL; 3868212SMichael.Corcoran@Sun.COM /* 3878212SMichael.Corcoran@Sun.COM * If we can't delete it now, mark it for later 3888212SMichael.Corcoran@Sun.COM */ 3898212SMichael.Corcoran@Sun.COM if (del->lv_refcnt) { 3908212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_delay_delete); 3918212SMichael.Corcoran@Sun.COM del->lv_flags |= LV_DEL; 3928212SMichael.Corcoran@Sun.COM del = NULL; 3938212SMichael.Corcoran@Sun.COM } 3948212SMichael.Corcoran@Sun.COM lvp = NULL; 3958212SMichael.Corcoran@Sun.COM } 3968212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 3978212SMichael.Corcoran@Sun.COM if (del) { 3988212SMichael.Corcoran@Sun.COM ASSERT(del->lv_refcnt == 0); 3998212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_find_delete); 4008212SMichael.Corcoran@Sun.COM lib_va_free(del); 4018212SMichael.Corcoran@Sun.COM } 4028212SMichael.Corcoran@Sun.COM return (lvp); 4038212SMichael.Corcoran@Sun.COM } 4048212SMichael.Corcoran@Sun.COM tmp = &lvp->lv_next; 4058212SMichael.Corcoran@Sun.COM } 4068212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 4078212SMichael.Corcoran@Sun.COM return (NULL); 4088212SMichael.Corcoran@Sun.COM } 4098212SMichael.Corcoran@Sun.COM 4108212SMichael.Corcoran@Sun.COM /* 4118212SMichael.Corcoran@Sun.COM * Add a new entry to the lib_va hash. 4128212SMichael.Corcoran@Sun.COM * Search the hash while holding the appropriate mutex to make sure that the 4138212SMichael.Corcoran@Sun.COM * data is not already in the cache. If we find data that is in the cache 4148212SMichael.Corcoran@Sun.COM * already and has not been modified since last use, we return NULL. If it 4158212SMichael.Corcoran@Sun.COM * has been modified since last use, we will remove that entry from 4168212SMichael.Corcoran@Sun.COM * the hash and it will be deleted once it's reference count reaches zero. 4178212SMichael.Corcoran@Sun.COM * If there is no current entry in the hash we will add the new entry and 4188212SMichael.Corcoran@Sun.COM * return it to the caller who is responsible for calling lib_va_release to 4198212SMichael.Corcoran@Sun.COM * drop their reference count on it. 4208212SMichael.Corcoran@Sun.COM * 4218212SMichael.Corcoran@Sun.COM * lv_num_segs will be set to zero since the caller needs to add that 4228212SMichael.Corcoran@Sun.COM * information to the data structure. 4238212SMichael.Corcoran@Sun.COM */ 4248212SMichael.Corcoran@Sun.COM static struct lib_va * 4258212SMichael.Corcoran@Sun.COM lib_va_add_hash(caddr_t base_va, ssize_t len, size_t align, vattr_t *vap) 4268212SMichael.Corcoran@Sun.COM { 4278212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 4288212SMichael.Corcoran@Sun.COM uint_t index; 4298212SMichael.Corcoran@Sun.COM model_t model; 4308212SMichael.Corcoran@Sun.COM struct lib_va **tmp; 4318212SMichael.Corcoran@Sun.COM struct lib_va *del = NULL; 4328212SMichael.Corcoran@Sun.COM 4338212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 4348212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(vap->va_nodeid); 4358212SMichael.Corcoran@Sun.COM 4368212SMichael.Corcoran@Sun.COM lvp = kmem_alloc(sizeof (struct lib_va), KM_SLEEP); 4378212SMichael.Corcoran@Sun.COM 4388212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 4398212SMichael.Corcoran@Sun.COM 4408212SMichael.Corcoran@Sun.COM /* 4418212SMichael.Corcoran@Sun.COM * Make sure not adding same data a second time. 4428212SMichael.Corcoran@Sun.COM * The hash chains should be relatively short and adding 4438212SMichael.Corcoran@Sun.COM * is a relatively rare event, so it's worth the check. 4448212SMichael.Corcoran@Sun.COM */ 4458212SMichael.Corcoran@Sun.COM tmp = &lib_va_hash[index]; 4468212SMichael.Corcoran@Sun.COM while (*tmp != NULL) { 4478212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_ID(*tmp, vap)) { 4488212SMichael.Corcoran@Sun.COM if (LIB_VA_MATCH_TIME(*tmp, vap)) { 4498212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 4508212SMichael.Corcoran@Sun.COM kmem_free(lvp, sizeof (struct lib_va)); 4518212SMichael.Corcoran@Sun.COM return (NULL); 4528212SMichael.Corcoran@Sun.COM } 4538212SMichael.Corcoran@Sun.COM 4548212SMichael.Corcoran@Sun.COM /* 4558212SMichael.Corcoran@Sun.COM * We have the same nodeid and fsid but the file has 4568212SMichael.Corcoran@Sun.COM * been modified since we last saw it. 4578212SMichael.Corcoran@Sun.COM * Need to remove the old node and add this new 4588212SMichael.Corcoran@Sun.COM * one. 4598212SMichael.Corcoran@Sun.COM * Could probably use a callback mechanism to make 4608212SMichael.Corcoran@Sun.COM * this cleaner. 4618212SMichael.Corcoran@Sun.COM */ 4628212SMichael.Corcoran@Sun.COM ASSERT(del == NULL); 4638212SMichael.Corcoran@Sun.COM del = *tmp; 4648212SMichael.Corcoran@Sun.COM *tmp = del->lv_next; 4658212SMichael.Corcoran@Sun.COM del->lv_next = NULL; 4668212SMichael.Corcoran@Sun.COM 4678212SMichael.Corcoran@Sun.COM /* 4688212SMichael.Corcoran@Sun.COM * Check to see if we can free it. If lv_refcnt 4698212SMichael.Corcoran@Sun.COM * is greater than zero, than some other thread 4708212SMichael.Corcoran@Sun.COM * has a reference to the one we want to delete 4718212SMichael.Corcoran@Sun.COM * and we can not delete it. All of this is done 4728212SMichael.Corcoran@Sun.COM * under the lib_va_hash_mutex lock so it is atomic. 4738212SMichael.Corcoran@Sun.COM */ 4748212SMichael.Corcoran@Sun.COM if (del->lv_refcnt) { 4758212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_add_delay_delete); 4768212SMichael.Corcoran@Sun.COM del->lv_flags |= LV_DEL; 4778212SMichael.Corcoran@Sun.COM del = NULL; 4788212SMichael.Corcoran@Sun.COM } 4798212SMichael.Corcoran@Sun.COM /* tmp is already advanced */ 4808212SMichael.Corcoran@Sun.COM continue; 4818212SMichael.Corcoran@Sun.COM } 4828212SMichael.Corcoran@Sun.COM tmp = &((*tmp)->lv_next); 4838212SMichael.Corcoran@Sun.COM } 4848212SMichael.Corcoran@Sun.COM 4858212SMichael.Corcoran@Sun.COM lvp->lv_base_va = base_va; 4868212SMichael.Corcoran@Sun.COM lvp->lv_len = len; 4878212SMichael.Corcoran@Sun.COM lvp->lv_align = align; 4888212SMichael.Corcoran@Sun.COM lvp->lv_nodeid = vap->va_nodeid; 4898212SMichael.Corcoran@Sun.COM lvp->lv_fsid = vap->va_fsid; 4908212SMichael.Corcoran@Sun.COM lvp->lv_ctime.tv_sec = vap->va_ctime.tv_sec; 4918212SMichael.Corcoran@Sun.COM lvp->lv_ctime.tv_nsec = vap->va_ctime.tv_nsec; 4928212SMichael.Corcoran@Sun.COM lvp->lv_mtime.tv_sec = vap->va_mtime.tv_sec; 4938212SMichael.Corcoran@Sun.COM lvp->lv_mtime.tv_nsec = vap->va_mtime.tv_nsec; 4948212SMichael.Corcoran@Sun.COM lvp->lv_next = NULL; 4958212SMichael.Corcoran@Sun.COM lvp->lv_refcnt = 1; 4968212SMichael.Corcoran@Sun.COM 4978212SMichael.Corcoran@Sun.COM /* Caller responsible for filling this and lv_mps out */ 4988212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = 0; 4998212SMichael.Corcoran@Sun.COM 5008212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 5018212SMichael.Corcoran@Sun.COM lvp->lv_flags = LV_ELF64; 5028212SMichael.Corcoran@Sun.COM } else { 5038212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 5048212SMichael.Corcoran@Sun.COM lvp->lv_flags = LV_ELF32; 5058212SMichael.Corcoran@Sun.COM } 5068212SMichael.Corcoran@Sun.COM 5078212SMichael.Corcoran@Sun.COM if (base_va != NULL) { 5088212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 5098212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_64, 1); 5108212SMichael.Corcoran@Sun.COM } else { 5118212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 5128212SMichael.Corcoran@Sun.COM atomic_add_32(&libs_mapped_32, 1); 5138212SMichael.Corcoran@Sun.COM } 5148212SMichael.Corcoran@Sun.COM } 5158212SMichael.Corcoran@Sun.COM ASSERT(*tmp == NULL); 5168212SMichael.Corcoran@Sun.COM *tmp = lvp; 5178212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 5188212SMichael.Corcoran@Sun.COM if (del) { 5198212SMichael.Corcoran@Sun.COM ASSERT(del->lv_refcnt == 0); 5208212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lib_va_add_delete); 5218212SMichael.Corcoran@Sun.COM lib_va_free(del); 5228212SMichael.Corcoran@Sun.COM } 5238212SMichael.Corcoran@Sun.COM return (lvp); 5248212SMichael.Corcoran@Sun.COM } 5258212SMichael.Corcoran@Sun.COM 5268212SMichael.Corcoran@Sun.COM /* 5278212SMichael.Corcoran@Sun.COM * Release the hold on lvp which was acquired by lib_va_find or lib_va_add_hash. 5288212SMichael.Corcoran@Sun.COM * In addition, if this is the last hold and lvp is marked for deletion, 5298212SMichael.Corcoran@Sun.COM * free up it's reserved address space and free the structure. 5308212SMichael.Corcoran@Sun.COM */ 5318212SMichael.Corcoran@Sun.COM static void 5328212SMichael.Corcoran@Sun.COM lib_va_release(struct lib_va *lvp) 5338212SMichael.Corcoran@Sun.COM { 5348212SMichael.Corcoran@Sun.COM uint_t index; 5358212SMichael.Corcoran@Sun.COM int to_del = 0; 5368212SMichael.Corcoran@Sun.COM 5378212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_refcnt > 0); 5388212SMichael.Corcoran@Sun.COM 5398212SMichael.Corcoran@Sun.COM index = LIB_VA_HASH(lvp->lv_nodeid); 5408212SMichael.Corcoran@Sun.COM mutex_enter(LIB_VA_HASH_MUTEX(index)); 5418212SMichael.Corcoran@Sun.COM if (--lvp->lv_refcnt == 0 && (lvp->lv_flags & LV_DEL)) { 5428212SMichael.Corcoran@Sun.COM to_del = 1; 5438212SMichael.Corcoran@Sun.COM } 5448212SMichael.Corcoran@Sun.COM mutex_exit(LIB_VA_HASH_MUTEX(index)); 5458212SMichael.Corcoran@Sun.COM if (to_del) { 5468212SMichael.Corcoran@Sun.COM ASSERT(lvp->lv_next == 0); 5478212SMichael.Corcoran@Sun.COM lib_va_free(lvp); 5488212SMichael.Corcoran@Sun.COM } 5498212SMichael.Corcoran@Sun.COM } 5508212SMichael.Corcoran@Sun.COM 5518212SMichael.Corcoran@Sun.COM /* 5528212SMichael.Corcoran@Sun.COM * Dummy function for mapping through /dev/null 5538212SMichael.Corcoran@Sun.COM * Normally I would have used mmmmap in common/io/mem.c 5548212SMichael.Corcoran@Sun.COM * but that is a static function, and for /dev/null, it 5558212SMichael.Corcoran@Sun.COM * just returns -1. 5568212SMichael.Corcoran@Sun.COM */ 5578212SMichael.Corcoran@Sun.COM /* ARGSUSED */ 5588212SMichael.Corcoran@Sun.COM static int 5598212SMichael.Corcoran@Sun.COM mmapobj_dummy(dev_t dev, off_t off, int prot) 5608212SMichael.Corcoran@Sun.COM { 5618212SMichael.Corcoran@Sun.COM return (-1); 5628212SMichael.Corcoran@Sun.COM } 5638212SMichael.Corcoran@Sun.COM 5648212SMichael.Corcoran@Sun.COM /* 5658212SMichael.Corcoran@Sun.COM * Called when an error occurred which requires mmapobj to return failure. 5668212SMichael.Corcoran@Sun.COM * All mapped objects will be unmapped and /dev/null mappings will be 5678212SMichael.Corcoran@Sun.COM * reclaimed if necessary. 5688212SMichael.Corcoran@Sun.COM * num_mapped is the number of elements of mrp which have been mapped, and 5698212SMichael.Corcoran@Sun.COM * num_segs is the total number of elements in mrp. 5708212SMichael.Corcoran@Sun.COM * For e_type ET_EXEC, we need to unmap all of the elements in mrp since 5718212SMichael.Corcoran@Sun.COM * we had already made reservations for them. 5728212SMichael.Corcoran@Sun.COM * If num_mapped equals num_segs, then we know that we had fully mapped 5738212SMichael.Corcoran@Sun.COM * the file and only need to clean up the segments described. 5748212SMichael.Corcoran@Sun.COM * If they are not equal, then for ET_DYN we will unmap the range from the 5758212SMichael.Corcoran@Sun.COM * end of the last mapped segment to the end of the last segment in mrp 5768212SMichael.Corcoran@Sun.COM * since we would have made a reservation for that memory earlier. 5778212SMichael.Corcoran@Sun.COM * If e_type is passed in as zero, num_mapped must equal num_segs. 5788212SMichael.Corcoran@Sun.COM */ 5798212SMichael.Corcoran@Sun.COM void 5808212SMichael.Corcoran@Sun.COM mmapobj_unmap(mmapobj_result_t *mrp, int num_mapped, int num_segs, 5818212SMichael.Corcoran@Sun.COM ushort_t e_type) 5828212SMichael.Corcoran@Sun.COM { 5838212SMichael.Corcoran@Sun.COM int i; 5848212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 5858212SMichael.Corcoran@Sun.COM caddr_t addr; 5868212SMichael.Corcoran@Sun.COM size_t size; 5878212SMichael.Corcoran@Sun.COM 5888212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 5898212SMichael.Corcoran@Sun.COM num_mapped = num_segs; 5908212SMichael.Corcoran@Sun.COM } 5918212SMichael.Corcoran@Sun.COM #ifdef DEBUG 5928212SMichael.Corcoran@Sun.COM if (e_type == 0) { 5938212SMichael.Corcoran@Sun.COM ASSERT(num_mapped == num_segs); 5948212SMichael.Corcoran@Sun.COM } 5958212SMichael.Corcoran@Sun.COM #endif 5968212SMichael.Corcoran@Sun.COM 5978212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unmap_called); 5988212SMichael.Corcoran@Sun.COM for (i = 0; i < num_mapped; i++) { 5998212SMichael.Corcoran@Sun.COM 6008212SMichael.Corcoran@Sun.COM /* 6018212SMichael.Corcoran@Sun.COM * If we are going to have to create a mapping we need to 6028212SMichael.Corcoran@Sun.COM * make sure that no one else will use the address we 6038212SMichael.Corcoran@Sun.COM * need to remap between the time it is unmapped and 6048212SMichael.Corcoran@Sun.COM * mapped below. 6058212SMichael.Corcoran@Sun.COM */ 6068212SMichael.Corcoran@Sun.COM if (mrp[i].mr_flags & MR_RESV) { 6078212SMichael.Corcoran@Sun.COM as_rangelock(as); 6088212SMichael.Corcoran@Sun.COM } 6098212SMichael.Corcoran@Sun.COM /* Always need to unmap what we mapped */ 6108212SMichael.Corcoran@Sun.COM (void) as_unmap(as, mrp[i].mr_addr, mrp[i].mr_msize); 6118212SMichael.Corcoran@Sun.COM 6128212SMichael.Corcoran@Sun.COM /* Need to reclaim /dev/null reservation from earlier */ 6138212SMichael.Corcoran@Sun.COM if (mrp[i].mr_flags & MR_RESV) { 6148212SMichael.Corcoran@Sun.COM struct segdev_crargs dev_a; 6158212SMichael.Corcoran@Sun.COM 6168212SMichael.Corcoran@Sun.COM ASSERT(e_type != ET_DYN); 6178212SMichael.Corcoran@Sun.COM /* 6188212SMichael.Corcoran@Sun.COM * Use seg_dev segment driver for /dev/null mapping. 6198212SMichael.Corcoran@Sun.COM */ 6208212SMichael.Corcoran@Sun.COM dev_a.mapfunc = mmapobj_dummy; 6218212SMichael.Corcoran@Sun.COM dev_a.dev = makedevice(mm_major, M_NULL); 6228212SMichael.Corcoran@Sun.COM dev_a.offset = 0; 6238212SMichael.Corcoran@Sun.COM dev_a.type = 0; /* neither PRIVATE nor SHARED */ 6248212SMichael.Corcoran@Sun.COM dev_a.prot = dev_a.maxprot = (uchar_t)PROT_NONE; 6258212SMichael.Corcoran@Sun.COM dev_a.hat_attr = 0; 6268212SMichael.Corcoran@Sun.COM dev_a.hat_flags = 0; 6278212SMichael.Corcoran@Sun.COM 6288212SMichael.Corcoran@Sun.COM (void) as_map(as, mrp[i].mr_addr, mrp[i].mr_msize, 6298212SMichael.Corcoran@Sun.COM segdev_create, &dev_a); 6308212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(remap_devnull); 6318212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 6328212SMichael.Corcoran@Sun.COM } 6338212SMichael.Corcoran@Sun.COM } 6348212SMichael.Corcoran@Sun.COM 6358212SMichael.Corcoran@Sun.COM if (num_mapped != num_segs) { 6368212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_DYN); 6378212SMichael.Corcoran@Sun.COM /* Need to unmap any reservation made after last mapped seg */ 6388212SMichael.Corcoran@Sun.COM if (num_mapped == 0) { 6398212SMichael.Corcoran@Sun.COM addr = mrp[0].mr_addr; 6408212SMichael.Corcoran@Sun.COM } else { 6418212SMichael.Corcoran@Sun.COM addr = mrp[num_mapped - 1].mr_addr + 6428212SMichael.Corcoran@Sun.COM mrp[num_mapped - 1].mr_msize; 6438212SMichael.Corcoran@Sun.COM } 6448212SMichael.Corcoran@Sun.COM size = (size_t)mrp[num_segs - 1].mr_addr + 6458212SMichael.Corcoran@Sun.COM mrp[num_segs - 1].mr_msize - (size_t)addr; 6468212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 6478212SMichael.Corcoran@Sun.COM 6488212SMichael.Corcoran@Sun.COM /* 6498212SMichael.Corcoran@Sun.COM * Now we need to unmap the holes between mapped segs. 6508212SMichael.Corcoran@Sun.COM * Note that we have not mapped all of the segments and thus 6518212SMichael.Corcoran@Sun.COM * the holes between segments would not have been unmapped 6528212SMichael.Corcoran@Sun.COM * yet. If num_mapped == num_segs, then all of the holes 6538212SMichael.Corcoran@Sun.COM * between segments would have already been unmapped. 6548212SMichael.Corcoran@Sun.COM */ 6558212SMichael.Corcoran@Sun.COM 6568212SMichael.Corcoran@Sun.COM for (i = 1; i < num_mapped; i++) { 6578212SMichael.Corcoran@Sun.COM addr = mrp[i - 1].mr_addr + mrp[i - 1].mr_msize; 6588212SMichael.Corcoran@Sun.COM size = mrp[i].mr_addr - addr; 6598212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 6608212SMichael.Corcoran@Sun.COM } 6618212SMichael.Corcoran@Sun.COM } 6628212SMichael.Corcoran@Sun.COM } 6638212SMichael.Corcoran@Sun.COM 6648212SMichael.Corcoran@Sun.COM /* 6658212SMichael.Corcoran@Sun.COM * We need to add the start address into mrp so that the unmap function 6668212SMichael.Corcoran@Sun.COM * has absolute addresses to use. 6678212SMichael.Corcoran@Sun.COM */ 6688212SMichael.Corcoran@Sun.COM static void 6698212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mmapobj_result_t *mrp, int num_mapped, caddr_t start_addr) 6708212SMichael.Corcoran@Sun.COM { 6718212SMichael.Corcoran@Sun.COM int i; 6728212SMichael.Corcoran@Sun.COM 6738212SMichael.Corcoran@Sun.COM for (i = 0; i < num_mapped; i++) { 6748212SMichael.Corcoran@Sun.COM mrp[i].mr_addr += (size_t)start_addr; 6758212SMichael.Corcoran@Sun.COM } 6768212SMichael.Corcoran@Sun.COM mmapobj_unmap(mrp, num_mapped, num_mapped, ET_EXEC); 6778212SMichael.Corcoran@Sun.COM } 6788212SMichael.Corcoran@Sun.COM 6798212SMichael.Corcoran@Sun.COM static caddr_t 6808212SMichael.Corcoran@Sun.COM mmapobj_lookup_start_addr(struct lib_va *lvp) 6818212SMichael.Corcoran@Sun.COM { 6828212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 6838212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 6848212SMichael.Corcoran@Sun.COM int error; 6858212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 6868212SMichael.Corcoran@Sun.COM caddr_t base = NULL; 6878212SMichael.Corcoran@Sun.COM size_t len; 6888212SMichael.Corcoran@Sun.COM size_t align; 6898212SMichael.Corcoran@Sun.COM 6908212SMichael.Corcoran@Sun.COM ASSERT(lvp != NULL); 6918212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lookup_start); 6928212SMichael.Corcoran@Sun.COM 6938212SMichael.Corcoran@Sun.COM as_rangelock(as); 6948212SMichael.Corcoran@Sun.COM 6958212SMichael.Corcoran@Sun.COM base = lvp->lv_base_va; 6968212SMichael.Corcoran@Sun.COM len = lvp->lv_len; 6978212SMichael.Corcoran@Sun.COM 6988212SMichael.Corcoran@Sun.COM /* 6998212SMichael.Corcoran@Sun.COM * If we don't have an expected base address, or the one that we want 7008212SMichael.Corcoran@Sun.COM * to use is not available, go get an acceptable address range. 7018212SMichael.Corcoran@Sun.COM */ 7028212SMichael.Corcoran@Sun.COM if (base == NULL || as_gap(as, len, &base, &len, 0, NULL)) { 7038212SMichael.Corcoran@Sun.COM 7048212SMichael.Corcoran@Sun.COM if (lvp->lv_flags & LV_ELF64) { 7058212SMichael.Corcoran@Sun.COM ma_flags = 0; 7068212SMichael.Corcoran@Sun.COM } 7078212SMichael.Corcoran@Sun.COM 7088212SMichael.Corcoran@Sun.COM align = lvp->lv_align; 7098212SMichael.Corcoran@Sun.COM if (align > 1) { 7108212SMichael.Corcoran@Sun.COM ma_flags |= MAP_ALIGN; 7118212SMichael.Corcoran@Sun.COM } 7128212SMichael.Corcoran@Sun.COM 7138212SMichael.Corcoran@Sun.COM base = (caddr_t)align; 7148212SMichael.Corcoran@Sun.COM map_addr(&base, len, 0, 1, ma_flags); 7158212SMichael.Corcoran@Sun.COM } 7168212SMichael.Corcoran@Sun.COM 7178212SMichael.Corcoran@Sun.COM /* 7188212SMichael.Corcoran@Sun.COM * Need to reserve the address space we're going to use. 7198212SMichael.Corcoran@Sun.COM * Don't reserve swap space since we'll be mapping over this. 7208212SMichael.Corcoran@Sun.COM */ 7218212SMichael.Corcoran@Sun.COM if (base != NULL) { 7228212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 7238212SMichael.Corcoran@Sun.COM error = as_map(as, base, len, segvn_create, &crargs); 7248212SMichael.Corcoran@Sun.COM if (error) { 7258212SMichael.Corcoran@Sun.COM base = NULL; 7268212SMichael.Corcoran@Sun.COM } 7278212SMichael.Corcoran@Sun.COM } 7288212SMichael.Corcoran@Sun.COM 7298212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 7308212SMichael.Corcoran@Sun.COM return (base); 7318212SMichael.Corcoran@Sun.COM } 7328212SMichael.Corcoran@Sun.COM 7338212SMichael.Corcoran@Sun.COM /* 7348212SMichael.Corcoran@Sun.COM * Get the starting address for a given file to be mapped and return it 7358212SMichael.Corcoran@Sun.COM * to the caller. If we're using lib_va and we need to allocate an address, 7368212SMichael.Corcoran@Sun.COM * we will attempt to allocate it from the global reserved pool such that the 7378212SMichael.Corcoran@Sun.COM * same address can be used in the future for this file. If we can't use the 7388212SMichael.Corcoran@Sun.COM * reserved address then we just get one that will fit in our address space. 7398212SMichael.Corcoran@Sun.COM * 7408212SMichael.Corcoran@Sun.COM * Returns the starting virtual address for the range to be mapped or NULL 7418212SMichael.Corcoran@Sun.COM * if an error is encountered. If we successfully insert the requested info 7428212SMichael.Corcoran@Sun.COM * into the lib_va hash, then *lvpp will be set to point to this lib_va 7438212SMichael.Corcoran@Sun.COM * structure. The structure will have a hold on it and thus lib_va_release 7448212SMichael.Corcoran@Sun.COM * needs to be called on it by the caller. This function will not fill out 7458212SMichael.Corcoran@Sun.COM * lv_mps or lv_num_segs since it does not have enough information to do so. 7468212SMichael.Corcoran@Sun.COM * The caller is responsible for doing this making sure that any modifications 7478212SMichael.Corcoran@Sun.COM * to lv_mps are visible before setting lv_num_segs. 7488212SMichael.Corcoran@Sun.COM */ 7498212SMichael.Corcoran@Sun.COM static caddr_t 7508212SMichael.Corcoran@Sun.COM mmapobj_alloc_start_addr(struct lib_va **lvpp, size_t len, int use_lib_va, 7518212SMichael.Corcoran@Sun.COM size_t align, vattr_t *vap) 7528212SMichael.Corcoran@Sun.COM { 7538212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 7548212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 7558212SMichael.Corcoran@Sun.COM int error; 7568212SMichael.Corcoran@Sun.COM model_t model; 7578212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 7588212SMichael.Corcoran@Sun.COM caddr_t base = NULL; 7598212SMichael.Corcoran@Sun.COM vmem_t *model_vmem; 7608212SMichael.Corcoran@Sun.COM 7618212SMichael.Corcoran@Sun.COM ASSERT(lvpp != NULL); 7628212SMichael.Corcoran@Sun.COM 7638212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_start); 7648212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 7658212SMichael.Corcoran@Sun.COM 7668212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 7678212SMichael.Corcoran@Sun.COM ma_flags = 0; 7688212SMichael.Corcoran@Sun.COM model_vmem = lib_va_64_arena; 7698212SMichael.Corcoran@Sun.COM } else { 7708212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 7718212SMichael.Corcoran@Sun.COM model_vmem = lib_va_32_arena; 7728212SMichael.Corcoran@Sun.COM } 7738212SMichael.Corcoran@Sun.COM 7748212SMichael.Corcoran@Sun.COM if (align > 1) { 7758212SMichael.Corcoran@Sun.COM ma_flags |= MAP_ALIGN; 7768212SMichael.Corcoran@Sun.COM } 7778212SMichael.Corcoran@Sun.COM if (use_lib_va) { 7788212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64 || libs_mapped_32 < lib_threshold) { 7798212SMichael.Corcoran@Sun.COM base = vmem_xalloc(model_vmem, len, align, 0, 0, NULL, 7808212SMichael.Corcoran@Sun.COM NULL, VM_NOSLEEP | VM_ENDALLOC); 7818212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_vmem); 7828212SMichael.Corcoran@Sun.COM } 7838212SMichael.Corcoran@Sun.COM #ifdef DEBUG 7848212SMichael.Corcoran@Sun.COM /* 7858212SMichael.Corcoran@Sun.COM * Check to see if we've run into ld.so.1. 7868212SMichael.Corcoran@Sun.COM * If this is the first library we've mapped and we can not 7878212SMichael.Corcoran@Sun.COM * use our reserved address space, then it's likely that 7888212SMichael.Corcoran@Sun.COM * ld.so.1 is occupying some of this space and the 7898212SMichael.Corcoran@Sun.COM * model_vmem arena bounds need to be changed. If we've run 7908212SMichael.Corcoran@Sun.COM * into something else besides ld.so.1 we'll see this message 7918212SMichael.Corcoran@Sun.COM * on the first use of mmapobj and should ignore the message. 7928212SMichael.Corcoran@Sun.COM */ 7938212SMichael.Corcoran@Sun.COM if (base != NULL && libs_mapped_32 == 0 && 7948212SMichael.Corcoran@Sun.COM model == DATAMODEL_ILP32 && 7958212SMichael.Corcoran@Sun.COM as_gap(as, len, &base, &len, 0, NULL)) { 7968212SMichael.Corcoran@Sun.COM cmn_err(CE_NOTE, 7978212SMichael.Corcoran@Sun.COM "lib_va_32_arena may not be optimized"); 7988212SMichael.Corcoran@Sun.COM } else if (base != NULL && libs_mapped_64 == 0 && 7998212SMichael.Corcoran@Sun.COM model == DATAMODEL_LP64 && 8008212SMichael.Corcoran@Sun.COM as_gap(as, len, &base, &len, 0, NULL)) { 8018212SMichael.Corcoran@Sun.COM cmn_err(CE_NOTE, 8028212SMichael.Corcoran@Sun.COM "lib_va_64_arena may not be optimized"); 8038212SMichael.Corcoran@Sun.COM } 8048212SMichael.Corcoran@Sun.COM #endif 8058212SMichael.Corcoran@Sun.COM /* 8068212SMichael.Corcoran@Sun.COM * Even if the address fails to fit in our address space, 8078212SMichael.Corcoran@Sun.COM * or we can't use a reserved address, 8088212SMichael.Corcoran@Sun.COM * we should still save it off in lib_va_hash. 8098212SMichael.Corcoran@Sun.COM */ 8108212SMichael.Corcoran@Sun.COM *lvpp = lib_va_add_hash(base, len, align, vap); 8118212SMichael.Corcoran@Sun.COM 8128212SMichael.Corcoran@Sun.COM /* 8138212SMichael.Corcoran@Sun.COM * Check for collision on insertion and free up our VA space. 8148212SMichael.Corcoran@Sun.COM * This is expected to be rare, so we'll just reset base to 8158212SMichael.Corcoran@Sun.COM * NULL instead of looking it up in the lib_va hash. 8168212SMichael.Corcoran@Sun.COM */ 8178212SMichael.Corcoran@Sun.COM if (*lvpp == NULL) { 8188212SMichael.Corcoran@Sun.COM if (base != NULL) { 8198212SMichael.Corcoran@Sun.COM vmem_xfree(model_vmem, base, len); 8208212SMichael.Corcoran@Sun.COM base = NULL; 8218212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(add_collision); 8228212SMichael.Corcoran@Sun.COM } 8238212SMichael.Corcoran@Sun.COM } 8248212SMichael.Corcoran@Sun.COM } 8258212SMichael.Corcoran@Sun.COM 8268212SMichael.Corcoran@Sun.COM as_rangelock(as); 8278212SMichael.Corcoran@Sun.COM 8288212SMichael.Corcoran@Sun.COM /* 8298212SMichael.Corcoran@Sun.COM * If we don't have an expected base address, or the one that we want 8308212SMichael.Corcoran@Sun.COM * to use is not available, go get an acceptable address range. 8318212SMichael.Corcoran@Sun.COM */ 8328212SMichael.Corcoran@Sun.COM if (base == NULL || as_gap(as, len, &base, &len, 0, NULL)) { 8338212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(get_addr); 8348212SMichael.Corcoran@Sun.COM base = (caddr_t)align; 8358212SMichael.Corcoran@Sun.COM map_addr(&base, len, 0, 1, ma_flags); 8368212SMichael.Corcoran@Sun.COM } 8378212SMichael.Corcoran@Sun.COM 8388212SMichael.Corcoran@Sun.COM /* 8398212SMichael.Corcoran@Sun.COM * Need to reserve the address space we're going to use. 8408212SMichael.Corcoran@Sun.COM * Don't reserve swap space since we'll be mapping over this. 8418212SMichael.Corcoran@Sun.COM */ 8428212SMichael.Corcoran@Sun.COM if (base != NULL) { 8438212SMichael.Corcoran@Sun.COM /* Don't reserve swap space since we'll be mapping over this */ 8448212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 8458212SMichael.Corcoran@Sun.COM error = as_map(as, base, len, segvn_create, &crargs); 8468212SMichael.Corcoran@Sun.COM if (error) { 8478212SMichael.Corcoran@Sun.COM base = NULL; 8488212SMichael.Corcoran@Sun.COM } 8498212SMichael.Corcoran@Sun.COM } 8508212SMichael.Corcoran@Sun.COM 8518212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 8528212SMichael.Corcoran@Sun.COM return (base); 8538212SMichael.Corcoran@Sun.COM } 8548212SMichael.Corcoran@Sun.COM 8558212SMichael.Corcoran@Sun.COM /* 8568212SMichael.Corcoran@Sun.COM * Map the file associated with vp into the address space as a single 8578212SMichael.Corcoran@Sun.COM * read only private mapping. 8588212SMichael.Corcoran@Sun.COM * Returns 0 for success, and non-zero for failure to map the file. 8598212SMichael.Corcoran@Sun.COM */ 8608212SMichael.Corcoran@Sun.COM static int 8618212SMichael.Corcoran@Sun.COM mmapobj_map_flat(vnode_t *vp, mmapobj_result_t *mrp, size_t padding, 8628212SMichael.Corcoran@Sun.COM cred_t *fcred) 8638212SMichael.Corcoran@Sun.COM { 8648212SMichael.Corcoran@Sun.COM int error = 0; 8658212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 8668212SMichael.Corcoran@Sun.COM caddr_t addr = NULL; 8678212SMichael.Corcoran@Sun.COM caddr_t start_addr; 8688212SMichael.Corcoran@Sun.COM size_t len; 8698212SMichael.Corcoran@Sun.COM size_t pad_len; 8708212SMichael.Corcoran@Sun.COM int prot = PROT_USER | PROT_READ; 8718212SMichael.Corcoran@Sun.COM uint_t ma_flags = _MAP_LOW32; 8728212SMichael.Corcoran@Sun.COM vattr_t vattr; 8738212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_USER, PROT_ALL); 8748212SMichael.Corcoran@Sun.COM 8758212SMichael.Corcoran@Sun.COM if (get_udatamodel() == DATAMODEL_LP64) { 8768212SMichael.Corcoran@Sun.COM ma_flags = 0; 8778212SMichael.Corcoran@Sun.COM } 8788212SMichael.Corcoran@Sun.COM 8798212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_SIZE; 8808212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 8818212SMichael.Corcoran@Sun.COM if (error) { 8828212SMichael.Corcoran@Sun.COM return (error); 8838212SMichael.Corcoran@Sun.COM } 8848212SMichael.Corcoran@Sun.COM 8858212SMichael.Corcoran@Sun.COM len = vattr.va_size; 8868212SMichael.Corcoran@Sun.COM 8878212SMichael.Corcoran@Sun.COM ma_flags |= MAP_PRIVATE; 8888212SMichael.Corcoran@Sun.COM if (padding == 0) { 8898212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_flat_no_padding); 8908212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, 0, as, &addr, len, prot, PROT_ALL, 8918212SMichael.Corcoran@Sun.COM ma_flags, fcred, NULL); 8928212SMichael.Corcoran@Sun.COM if (error == 0) { 8938212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = addr; 8948212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = len; 8958212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = len; 8968212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 8978212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = prot; 8988212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = 0; 8998212SMichael.Corcoran@Sun.COM } 9008212SMichael.Corcoran@Sun.COM return (error); 9018212SMichael.Corcoran@Sun.COM } 9028212SMichael.Corcoran@Sun.COM 9038212SMichael.Corcoran@Sun.COM /* padding was requested so there's more work to be done */ 9048212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_flat_padding); 9058212SMichael.Corcoran@Sun.COM 9068212SMichael.Corcoran@Sun.COM /* No need to reserve swap space now since it will be reserved later */ 9078212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 9088212SMichael.Corcoran@Sun.COM 9098212SMichael.Corcoran@Sun.COM /* Need to setup padding which can only be in PAGESIZE increments. */ 9108212SMichael.Corcoran@Sun.COM ASSERT((padding & PAGEOFFSET) == 0); 9118212SMichael.Corcoran@Sun.COM pad_len = len + (2 * padding); 9128212SMichael.Corcoran@Sun.COM 9138212SMichael.Corcoran@Sun.COM as_rangelock(as); 9148212SMichael.Corcoran@Sun.COM map_addr(&addr, pad_len, 0, 1, ma_flags); 9158212SMichael.Corcoran@Sun.COM error = as_map(as, addr, pad_len, segvn_create, &crargs); 9168212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 9178212SMichael.Corcoran@Sun.COM if (error) { 9188212SMichael.Corcoran@Sun.COM return (error); 9198212SMichael.Corcoran@Sun.COM } 9208212SMichael.Corcoran@Sun.COM start_addr = addr; 9218212SMichael.Corcoran@Sun.COM addr += padding; 9228212SMichael.Corcoran@Sun.COM ma_flags |= MAP_FIXED; 9238212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, 0, as, &addr, len, prot, PROT_ALL, ma_flags, 9248212SMichael.Corcoran@Sun.COM fcred, NULL); 9258212SMichael.Corcoran@Sun.COM if (error == 0) { 9268212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = start_addr; 9278212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = padding; 9288212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = 0; 9298212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 9308212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = 0; 9318212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_PADDING; 9328212SMichael.Corcoran@Sun.COM 9338212SMichael.Corcoran@Sun.COM mrp[1].mr_addr = addr; 9348212SMichael.Corcoran@Sun.COM mrp[1].mr_msize = len; 9358212SMichael.Corcoran@Sun.COM mrp[1].mr_fsize = len; 9368212SMichael.Corcoran@Sun.COM mrp[1].mr_offset = 0; 9378212SMichael.Corcoran@Sun.COM mrp[1].mr_prot = prot; 9388212SMichael.Corcoran@Sun.COM mrp[1].mr_flags = 0; 9398212SMichael.Corcoran@Sun.COM 9408212SMichael.Corcoran@Sun.COM mrp[2].mr_addr = addr + P2ROUNDUP(len, PAGESIZE); 9418212SMichael.Corcoran@Sun.COM mrp[2].mr_msize = padding; 9428212SMichael.Corcoran@Sun.COM mrp[2].mr_fsize = 0; 9438212SMichael.Corcoran@Sun.COM mrp[2].mr_offset = 0; 9448212SMichael.Corcoran@Sun.COM mrp[2].mr_prot = 0; 9458212SMichael.Corcoran@Sun.COM mrp[2].mr_flags = MR_PADDING; 9468212SMichael.Corcoran@Sun.COM } else { 9478212SMichael.Corcoran@Sun.COM /* Need to cleanup the as_map from earlier */ 9488212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, pad_len); 9498212SMichael.Corcoran@Sun.COM } 9508212SMichael.Corcoran@Sun.COM return (error); 9518212SMichael.Corcoran@Sun.COM } 9528212SMichael.Corcoran@Sun.COM 9538212SMichael.Corcoran@Sun.COM /* 9548212SMichael.Corcoran@Sun.COM * Map a PT_LOAD or PT_SUNWBSS section of an executable file into the user's 9558212SMichael.Corcoran@Sun.COM * address space. 9568212SMichael.Corcoran@Sun.COM * vp - vnode to be mapped in 9578212SMichael.Corcoran@Sun.COM * addr - start address 9588212SMichael.Corcoran@Sun.COM * len - length of vp to be mapped 9598212SMichael.Corcoran@Sun.COM * zfodlen - length of zero filled memory after len above 9608212SMichael.Corcoran@Sun.COM * offset - offset into file where mapping should start 9618212SMichael.Corcoran@Sun.COM * prot - protections for this mapping 9628212SMichael.Corcoran@Sun.COM * fcred - credentials for the file associated with vp at open time. 9638212SMichael.Corcoran@Sun.COM */ 9648212SMichael.Corcoran@Sun.COM static int 9658212SMichael.Corcoran@Sun.COM mmapobj_map_ptload(struct vnode *vp, caddr_t addr, size_t len, size_t zfodlen, 9668212SMichael.Corcoran@Sun.COM off_t offset, int prot, cred_t *fcred) 9678212SMichael.Corcoran@Sun.COM { 9688212SMichael.Corcoran@Sun.COM int error = 0; 9698212SMichael.Corcoran@Sun.COM caddr_t zfodbase, oldaddr; 9708212SMichael.Corcoran@Sun.COM size_t oldlen; 9718212SMichael.Corcoran@Sun.COM size_t end; 9728212SMichael.Corcoran@Sun.COM size_t zfoddiff; 9738212SMichael.Corcoran@Sun.COM label_t ljb; 9748212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 9758212SMichael.Corcoran@Sun.COM model_t model; 9768212SMichael.Corcoran@Sun.COM int full_page; 9778212SMichael.Corcoran@Sun.COM 9788212SMichael.Corcoran@Sun.COM /* 9798212SMichael.Corcoran@Sun.COM * See if addr and offset are aligned such that we can map in 9808212SMichael.Corcoran@Sun.COM * full pages instead of partial pages. 9818212SMichael.Corcoran@Sun.COM */ 9828212SMichael.Corcoran@Sun.COM full_page = (((uintptr_t)addr & PAGEOFFSET) == 9838212SMichael.Corcoran@Sun.COM ((uintptr_t)offset & PAGEOFFSET)); 9848212SMichael.Corcoran@Sun.COM 9858212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 9868212SMichael.Corcoran@Sun.COM 9878212SMichael.Corcoran@Sun.COM oldaddr = addr; 9888212SMichael.Corcoran@Sun.COM addr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 9898212SMichael.Corcoran@Sun.COM if (len) { 9908212SMichael.Corcoran@Sun.COM spgcnt_t availm, npages; 9918212SMichael.Corcoran@Sun.COM int preread; 9928212SMichael.Corcoran@Sun.COM uint_t mflag = MAP_PRIVATE | MAP_FIXED; 9938212SMichael.Corcoran@Sun.COM 9948212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_ILP32) { 9958212SMichael.Corcoran@Sun.COM mflag |= _MAP_LOW32; 9968212SMichael.Corcoran@Sun.COM } 9978212SMichael.Corcoran@Sun.COM /* We may need to map in extra bytes */ 9988212SMichael.Corcoran@Sun.COM oldlen = len; 9998212SMichael.Corcoran@Sun.COM len += ((size_t)oldaddr & PAGEOFFSET); 10008212SMichael.Corcoran@Sun.COM 10018212SMichael.Corcoran@Sun.COM if (full_page) { 10028212SMichael.Corcoran@Sun.COM offset = (off_t)((uintptr_t)offset & PAGEMASK); 10038212SMichael.Corcoran@Sun.COM if ((prot & (PROT_WRITE | PROT_EXEC)) == PROT_EXEC) { 10048212SMichael.Corcoran@Sun.COM mflag |= MAP_TEXT; 10058212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_text); 10068212SMichael.Corcoran@Sun.COM } else { 10078212SMichael.Corcoran@Sun.COM mflag |= MAP_INITDATA; 10088212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_initdata); 10098212SMichael.Corcoran@Sun.COM } 10108212SMichael.Corcoran@Sun.COM 10118212SMichael.Corcoran@Sun.COM /* 10128212SMichael.Corcoran@Sun.COM * maxprot is passed as PROT_ALL so that mdb can 10138212SMichael.Corcoran@Sun.COM * write to this segment. 10148212SMichael.Corcoran@Sun.COM */ 10158212SMichael.Corcoran@Sun.COM if (error = VOP_MAP(vp, (offset_t)offset, as, &addr, 10168212SMichael.Corcoran@Sun.COM len, prot, PROT_ALL, mflag, fcred, NULL)) { 10178212SMichael.Corcoran@Sun.COM return (error); 10188212SMichael.Corcoran@Sun.COM } 10198212SMichael.Corcoran@Sun.COM 10208212SMichael.Corcoran@Sun.COM /* 10218212SMichael.Corcoran@Sun.COM * If the segment can fit and is relatively small, then 10228212SMichael.Corcoran@Sun.COM * we prefault the entire segment in. This is based 10238212SMichael.Corcoran@Sun.COM * on the model that says the best working set of a 10248212SMichael.Corcoran@Sun.COM * small program is all of its pages. 10258212SMichael.Corcoran@Sun.COM * We only do this if freemem will not drop below 10268212SMichael.Corcoran@Sun.COM * lotsfree since we don't want to induce paging. 10278212SMichael.Corcoran@Sun.COM */ 10288212SMichael.Corcoran@Sun.COM npages = (spgcnt_t)btopr(len); 10298212SMichael.Corcoran@Sun.COM availm = freemem - lotsfree; 10308212SMichael.Corcoran@Sun.COM preread = (npages < availm && len < PGTHRESH) ? 1 : 0; 10318212SMichael.Corcoran@Sun.COM 10328212SMichael.Corcoran@Sun.COM /* 10338212SMichael.Corcoran@Sun.COM * If we aren't prefaulting the segment, 10348212SMichael.Corcoran@Sun.COM * increment "deficit", if necessary to ensure 10358212SMichael.Corcoran@Sun.COM * that pages will become available when this 10368212SMichael.Corcoran@Sun.COM * process starts executing. 10378212SMichael.Corcoran@Sun.COM */ 10388212SMichael.Corcoran@Sun.COM if (preread == 0 && npages > availm && 10398212SMichael.Corcoran@Sun.COM deficit < lotsfree) { 10408212SMichael.Corcoran@Sun.COM deficit += MIN((pgcnt_t)(npages - availm), 10418212SMichael.Corcoran@Sun.COM lotsfree - deficit); 10428212SMichael.Corcoran@Sun.COM } 10438212SMichael.Corcoran@Sun.COM 10448212SMichael.Corcoran@Sun.COM if (preread) { 10458212SMichael.Corcoran@Sun.COM (void) as_faulta(as, addr, len); 10468212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_preread); 10478212SMichael.Corcoran@Sun.COM } 10488212SMichael.Corcoran@Sun.COM } else { 10498212SMichael.Corcoran@Sun.COM /* 10508212SMichael.Corcoran@Sun.COM * addr and offset were not aligned such that we could 10518212SMichael.Corcoran@Sun.COM * use VOP_MAP, thus we need to as_map the memory we 10528212SMichael.Corcoran@Sun.COM * need and then read the data in from disk. 10538212SMichael.Corcoran@Sun.COM * This code path is a corner case which should never 10548212SMichael.Corcoran@Sun.COM * be taken, but hand crafted binaries could trigger 10558212SMichael.Corcoran@Sun.COM * this logic and it needs to work correctly. 10568212SMichael.Corcoran@Sun.COM */ 10578212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_text); 10588212SMichael.Corcoran@Sun.COM as_rangelock(as); 10598212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, len); 10608212SMichael.Corcoran@Sun.COM 10618212SMichael.Corcoran@Sun.COM /* 10628212SMichael.Corcoran@Sun.COM * We use zfod_argsp because we need to be able to 10638212SMichael.Corcoran@Sun.COM * write to the mapping and then we'll change the 10648212SMichael.Corcoran@Sun.COM * protections later if they are incorrect. 10658212SMichael.Corcoran@Sun.COM */ 10668212SMichael.Corcoran@Sun.COM error = as_map(as, addr, len, segvn_create, zfod_argsp); 10678212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 10688212SMichael.Corcoran@Sun.COM if (error) { 10698212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_map_fail); 10708212SMichael.Corcoran@Sun.COM return (error); 10718212SMichael.Corcoran@Sun.COM } 10728212SMichael.Corcoran@Sun.COM 10738212SMichael.Corcoran@Sun.COM /* Now read in the data from disk */ 10748212SMichael.Corcoran@Sun.COM error = vn_rdwr(UIO_READ, vp, oldaddr, oldlen, offset, 10758212SMichael.Corcoran@Sun.COM UIO_USERSPACE, 0, (rlim64_t)0, fcred, NULL); 10768212SMichael.Corcoran@Sun.COM if (error) { 10778212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_ptload_unaligned_read_fail); 10788212SMichael.Corcoran@Sun.COM return (error); 10798212SMichael.Corcoran@Sun.COM } 10808212SMichael.Corcoran@Sun.COM 10818212SMichael.Corcoran@Sun.COM /* 10828212SMichael.Corcoran@Sun.COM * Now set protections. 10838212SMichael.Corcoran@Sun.COM */ 10848212SMichael.Corcoran@Sun.COM if (prot != PROT_ZFOD) { 10858212SMichael.Corcoran@Sun.COM (void) as_setprot(as, addr, len, prot); 10868212SMichael.Corcoran@Sun.COM } 10878212SMichael.Corcoran@Sun.COM } 10888212SMichael.Corcoran@Sun.COM } 10898212SMichael.Corcoran@Sun.COM 10908212SMichael.Corcoran@Sun.COM if (zfodlen) { 10918212SMichael.Corcoran@Sun.COM end = (size_t)addr + len; 10928212SMichael.Corcoran@Sun.COM zfodbase = (caddr_t)P2ROUNDUP(end, PAGESIZE); 10938212SMichael.Corcoran@Sun.COM zfoddiff = (uintptr_t)zfodbase - end; 10948212SMichael.Corcoran@Sun.COM if (zfoddiff) { 10958212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfoddiff); 10968212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 10978212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 10988212SMichael.Corcoran@Sun.COM zfoddiff, prot | PROT_WRITE); 10998212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfoddiff_nowrite); 11008212SMichael.Corcoran@Sun.COM } 11018212SMichael.Corcoran@Sun.COM if (on_fault(&ljb)) { 11028212SMichael.Corcoran@Sun.COM no_fault(); 11038212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 11048212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 11058212SMichael.Corcoran@Sun.COM zfoddiff, prot); 11068212SMichael.Corcoran@Sun.COM } 11078212SMichael.Corcoran@Sun.COM return (EFAULT); 11088212SMichael.Corcoran@Sun.COM } 11098212SMichael.Corcoran@Sun.COM uzero((void *)end, zfoddiff); 11108212SMichael.Corcoran@Sun.COM no_fault(); 11118212SMichael.Corcoran@Sun.COM 11128212SMichael.Corcoran@Sun.COM /* 11138212SMichael.Corcoran@Sun.COM * Remove write protection to return to original state 11148212SMichael.Corcoran@Sun.COM */ 11158212SMichael.Corcoran@Sun.COM if ((prot & PROT_WRITE) == 0) { 11168212SMichael.Corcoran@Sun.COM (void) as_setprot(as, (caddr_t)end, 11178212SMichael.Corcoran@Sun.COM zfoddiff, prot); 11188212SMichael.Corcoran@Sun.COM } 11198212SMichael.Corcoran@Sun.COM } 11208212SMichael.Corcoran@Sun.COM if (zfodlen > zfoddiff) { 11218212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = 11228212SMichael.Corcoran@Sun.COM SEGVN_ZFOD_ARGS(prot, PROT_ALL); 11238212SMichael.Corcoran@Sun.COM 11248212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(zfodextra); 11258212SMichael.Corcoran@Sun.COM zfodlen -= zfoddiff; 11268212SMichael.Corcoran@Sun.COM crargs.szc = AS_MAP_NO_LPOOB; 11278212SMichael.Corcoran@Sun.COM 11288212SMichael.Corcoran@Sun.COM 11298212SMichael.Corcoran@Sun.COM as_rangelock(as); 11308212SMichael.Corcoran@Sun.COM (void) as_unmap(as, (caddr_t)zfodbase, zfodlen); 11318212SMichael.Corcoran@Sun.COM error = as_map(as, (caddr_t)zfodbase, 11328212SMichael.Corcoran@Sun.COM zfodlen, segvn_create, &crargs); 11338212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 11348212SMichael.Corcoran@Sun.COM if (error) { 11358212SMichael.Corcoran@Sun.COM return (error); 11368212SMichael.Corcoran@Sun.COM } 11378212SMichael.Corcoran@Sun.COM } 11388212SMichael.Corcoran@Sun.COM } 11398212SMichael.Corcoran@Sun.COM return (0); 11408212SMichael.Corcoran@Sun.COM } 11418212SMichael.Corcoran@Sun.COM 11428212SMichael.Corcoran@Sun.COM /* 11438212SMichael.Corcoran@Sun.COM * Map the ELF file represented by vp into the users address space. The 11448212SMichael.Corcoran@Sun.COM * first mapping will start at start_addr and there will be num_elements 11458212SMichael.Corcoran@Sun.COM * mappings. The mappings are described by the data in mrp which may be 11468212SMichael.Corcoran@Sun.COM * modified upon returning from this function. 11478212SMichael.Corcoran@Sun.COM * Returns 0 for success or errno for failure. 11488212SMichael.Corcoran@Sun.COM */ 11498212SMichael.Corcoran@Sun.COM static int 11508212SMichael.Corcoran@Sun.COM mmapobj_map_elf(struct vnode *vp, caddr_t start_addr, mmapobj_result_t *mrp, 11518212SMichael.Corcoran@Sun.COM int num_elements, cred_t *fcred, ushort_t e_type) 11528212SMichael.Corcoran@Sun.COM { 11538212SMichael.Corcoran@Sun.COM int i; 11548212SMichael.Corcoran@Sun.COM int ret; 11558212SMichael.Corcoran@Sun.COM caddr_t lo; 11568212SMichael.Corcoran@Sun.COM caddr_t hi; 11578212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 11588212SMichael.Corcoran@Sun.COM 11598212SMichael.Corcoran@Sun.COM for (i = 0; i < num_elements; i++) { 11608212SMichael.Corcoran@Sun.COM caddr_t addr; 11618212SMichael.Corcoran@Sun.COM size_t p_memsz; 11628212SMichael.Corcoran@Sun.COM size_t p_filesz; 11638212SMichael.Corcoran@Sun.COM size_t zfodlen; 11648212SMichael.Corcoran@Sun.COM offset_t p_offset; 11658212SMichael.Corcoran@Sun.COM size_t dif; 11668212SMichael.Corcoran@Sun.COM int prot; 11678212SMichael.Corcoran@Sun.COM 11688212SMichael.Corcoran@Sun.COM /* Always need to adjust mr_addr */ 11698212SMichael.Corcoran@Sun.COM addr = start_addr + (size_t)(mrp[i].mr_addr); 11708212SMichael.Corcoran@Sun.COM mrp[i].mr_addr = 11718212SMichael.Corcoran@Sun.COM (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 11728212SMichael.Corcoran@Sun.COM 11738212SMichael.Corcoran@Sun.COM /* Padding has already been mapped */ 11748212SMichael.Corcoran@Sun.COM if (MR_GET_TYPE(mrp[i].mr_flags) == MR_PADDING) { 11758212SMichael.Corcoran@Sun.COM continue; 11768212SMichael.Corcoran@Sun.COM } 11778212SMichael.Corcoran@Sun.COM p_memsz = mrp[i].mr_msize; 11788212SMichael.Corcoran@Sun.COM p_filesz = mrp[i].mr_fsize; 11798212SMichael.Corcoran@Sun.COM zfodlen = p_memsz - p_filesz; 11808212SMichael.Corcoran@Sun.COM p_offset = mrp[i].mr_offset; 11818212SMichael.Corcoran@Sun.COM dif = (uintptr_t)(addr) & PAGEOFFSET; 11828212SMichael.Corcoran@Sun.COM prot = mrp[i].mr_prot | PROT_USER; 11838212SMichael.Corcoran@Sun.COM ret = mmapobj_map_ptload(vp, addr, p_filesz, zfodlen, 11848212SMichael.Corcoran@Sun.COM p_offset, prot, fcred); 11858212SMichael.Corcoran@Sun.COM if (ret != 0) { 11868212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(ptload_failed); 11878212SMichael.Corcoran@Sun.COM mmapobj_unmap(mrp, i, num_elements, e_type); 11888212SMichael.Corcoran@Sun.COM return (ret); 11898212SMichael.Corcoran@Sun.COM } 11908212SMichael.Corcoran@Sun.COM 11918212SMichael.Corcoran@Sun.COM /* Need to cleanup mrp to reflect the actual values used */ 11928212SMichael.Corcoran@Sun.COM mrp[i].mr_msize += dif; 11938212SMichael.Corcoran@Sun.COM mrp[i].mr_offset = (size_t)addr & PAGEOFFSET; 11948212SMichael.Corcoran@Sun.COM } 11958212SMichael.Corcoran@Sun.COM 11968212SMichael.Corcoran@Sun.COM /* Also need to unmap any holes created above */ 11978212SMichael.Corcoran@Sun.COM if (num_elements == 1) { 11988212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(map_elf_no_holes); 11998212SMichael.Corcoran@Sun.COM return (0); 12008212SMichael.Corcoran@Sun.COM } 12018212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 12028212SMichael.Corcoran@Sun.COM return (0); 12038212SMichael.Corcoran@Sun.COM } 12048212SMichael.Corcoran@Sun.COM 12058212SMichael.Corcoran@Sun.COM as_rangelock(as); 12068212SMichael.Corcoran@Sun.COM lo = start_addr; 12078212SMichael.Corcoran@Sun.COM hi = mrp[0].mr_addr; 12088212SMichael.Corcoran@Sun.COM 12098212SMichael.Corcoran@Sun.COM /* Remove holes made by the rest of the segments */ 12108212SMichael.Corcoran@Sun.COM for (i = 0; i < num_elements - 1; i++) { 12118212SMichael.Corcoran@Sun.COM lo = (caddr_t)P2ROUNDUP((size_t)(mrp[i].mr_addr) + 12128212SMichael.Corcoran@Sun.COM mrp[i].mr_msize, PAGESIZE); 12138212SMichael.Corcoran@Sun.COM hi = mrp[i + 1].mr_addr; 12148212SMichael.Corcoran@Sun.COM if (lo < hi) { 12158212SMichael.Corcoran@Sun.COM /* 12168212SMichael.Corcoran@Sun.COM * If as_unmap fails we just use up a bit of extra 12178212SMichael.Corcoran@Sun.COM * space 12188212SMichael.Corcoran@Sun.COM */ 12198212SMichael.Corcoran@Sun.COM (void) as_unmap(as, (caddr_t)lo, 12208212SMichael.Corcoran@Sun.COM (size_t)hi - (size_t)lo); 12218212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unmap_hole); 12228212SMichael.Corcoran@Sun.COM } 12238212SMichael.Corcoran@Sun.COM } 12248212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 12258212SMichael.Corcoran@Sun.COM 12268212SMichael.Corcoran@Sun.COM return (0); 12278212SMichael.Corcoran@Sun.COM } 12288212SMichael.Corcoran@Sun.COM 12298212SMichael.Corcoran@Sun.COM /* Ugly hack to get STRUCT_* macros to work below */ 12308212SMichael.Corcoran@Sun.COM struct myphdr { 12318212SMichael.Corcoran@Sun.COM Phdr x; /* native version */ 12328212SMichael.Corcoran@Sun.COM }; 12338212SMichael.Corcoran@Sun.COM 12348212SMichael.Corcoran@Sun.COM struct myphdr32 { 12358212SMichael.Corcoran@Sun.COM Elf32_Phdr x; 12368212SMichael.Corcoran@Sun.COM }; 12378212SMichael.Corcoran@Sun.COM 12388212SMichael.Corcoran@Sun.COM /* 12398212SMichael.Corcoran@Sun.COM * Calculate and return the number of loadable segments in the ELF Phdr 12408212SMichael.Corcoran@Sun.COM * represented by phdrbase as well as the len of the total mapping and 12418212SMichael.Corcoran@Sun.COM * the max alignment that is needed for a given segment. On success, 12428212SMichael.Corcoran@Sun.COM * 0 is returned, and *len, *loadable and *align have been filled out. 12438212SMichael.Corcoran@Sun.COM * On failure, errno will be returned, which in this case is ENOTSUP 12448212SMichael.Corcoran@Sun.COM * if we were passed an ELF file with overlapping segments. 12458212SMichael.Corcoran@Sun.COM */ 12468212SMichael.Corcoran@Sun.COM static int 12478212SMichael.Corcoran@Sun.COM calc_loadable(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, size_t *len, 12488212SMichael.Corcoran@Sun.COM int *loadable, size_t *align) 12498212SMichael.Corcoran@Sun.COM { 12508212SMichael.Corcoran@Sun.COM int i; 12518212SMichael.Corcoran@Sun.COM int hsize; 12528212SMichael.Corcoran@Sun.COM model_t model; 12538212SMichael.Corcoran@Sun.COM uint_t p_type; 12548212SMichael.Corcoran@Sun.COM offset_t p_offset; 12558212SMichael.Corcoran@Sun.COM size_t p_memsz; 12568212SMichael.Corcoran@Sun.COM size_t p_align; 12578212SMichael.Corcoran@Sun.COM caddr_t vaddr; 12588212SMichael.Corcoran@Sun.COM int num_segs = 0; 12598212SMichael.Corcoran@Sun.COM caddr_t start_addr = NULL; 12608212SMichael.Corcoran@Sun.COM caddr_t p_end = NULL; 12618212SMichael.Corcoran@Sun.COM size_t max_align = 0; 1262*8362SMichael.Corcoran@Sun.COM size_t min_align = PAGESIZE; /* needed for vmem_xalloc */ 12638212SMichael.Corcoran@Sun.COM STRUCT_HANDLE(myphdr, mph); 12648212SMichael.Corcoran@Sun.COM #if defined(__sparc) 12658212SMichael.Corcoran@Sun.COM extern int vac_size; 1266*8362SMichael.Corcoran@Sun.COM 1267*8362SMichael.Corcoran@Sun.COM /* 1268*8362SMichael.Corcoran@Sun.COM * Want to prevent aliasing by making the start address at least be 1269*8362SMichael.Corcoran@Sun.COM * aligned to vac_size. 1270*8362SMichael.Corcoran@Sun.COM */ 1271*8362SMichael.Corcoran@Sun.COM min_align = MAX(PAGESIZE, vac_size); 12728212SMichael.Corcoran@Sun.COM #endif 12738212SMichael.Corcoran@Sun.COM 12748212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 12758212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 12768212SMichael.Corcoran@Sun.COM 12778212SMichael.Corcoran@Sun.COM /* hsize alignment should have been checked before calling this func */ 12788212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 12798212SMichael.Corcoran@Sun.COM hsize = ehdrp->e_phentsize; 12808212SMichael.Corcoran@Sun.COM if (hsize & 7) { 12818212SMichael.Corcoran@Sun.COM return (ENOTSUP); 12828212SMichael.Corcoran@Sun.COM } 12838212SMichael.Corcoran@Sun.COM } else { 12848212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 12858212SMichael.Corcoran@Sun.COM hsize = ((Elf32_Ehdr *)ehdrp)->e_phentsize; 12868212SMichael.Corcoran@Sun.COM if (hsize & 3) { 12878212SMichael.Corcoran@Sun.COM return (ENOTSUP); 12888212SMichael.Corcoran@Sun.COM } 12898212SMichael.Corcoran@Sun.COM } 12908212SMichael.Corcoran@Sun.COM 12918212SMichael.Corcoran@Sun.COM /* 12928212SMichael.Corcoran@Sun.COM * Determine the span of all loadable segments and calculate the 12938212SMichael.Corcoran@Sun.COM * number of loadable segments. 12948212SMichael.Corcoran@Sun.COM */ 12958212SMichael.Corcoran@Sun.COM for (i = 0; i < nphdrs; i++) { 12968212SMichael.Corcoran@Sun.COM p_type = STRUCT_FGET(mph, x.p_type); 12978212SMichael.Corcoran@Sun.COM if (p_type == PT_LOAD || p_type == PT_SUNWBSS) { 12988212SMichael.Corcoran@Sun.COM vaddr = (caddr_t)(uintptr_t)STRUCT_FGET(mph, x.p_vaddr); 12998212SMichael.Corcoran@Sun.COM p_memsz = STRUCT_FGET(mph, x.p_memsz); 13008212SMichael.Corcoran@Sun.COM 13018212SMichael.Corcoran@Sun.COM /* 13028212SMichael.Corcoran@Sun.COM * Skip this header if it requests no memory to be 13038212SMichael.Corcoran@Sun.COM * mapped. 13048212SMichael.Corcoran@Sun.COM */ 13058212SMichael.Corcoran@Sun.COM if (p_memsz == 0) { 13068212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 13078212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 13088212SMichael.Corcoran@Sun.COM hsize)); 13098212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(nomem_header); 13108212SMichael.Corcoran@Sun.COM continue; 13118212SMichael.Corcoran@Sun.COM } 13128212SMichael.Corcoran@Sun.COM if (num_segs++ == 0) { 13138212SMichael.Corcoran@Sun.COM start_addr = vaddr; 13148212SMichael.Corcoran@Sun.COM /* 13158212SMichael.Corcoran@Sun.COM * For the first segment, we need to map from 13168212SMichael.Corcoran@Sun.COM * the beginning of the file, so we will 13178212SMichael.Corcoran@Sun.COM * adjust the size of the mapping to include 13188212SMichael.Corcoran@Sun.COM * this memory. 13198212SMichael.Corcoran@Sun.COM */ 13208212SMichael.Corcoran@Sun.COM p_offset = STRUCT_FGET(mph, x.p_offset); 13218212SMichael.Corcoran@Sun.COM } else { 13228212SMichael.Corcoran@Sun.COM p_offset = 0; 13238212SMichael.Corcoran@Sun.COM } 13248212SMichael.Corcoran@Sun.COM /* 13258212SMichael.Corcoran@Sun.COM * Check to make sure that this mapping wouldn't 13268212SMichael.Corcoran@Sun.COM * overlap a previous mapping. 13278212SMichael.Corcoran@Sun.COM */ 13288212SMichael.Corcoran@Sun.COM if (vaddr < p_end) { 13298212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(overlap_header); 13308212SMichael.Corcoran@Sun.COM return (ENOTSUP); 13318212SMichael.Corcoran@Sun.COM } 13328212SMichael.Corcoran@Sun.COM 13338212SMichael.Corcoran@Sun.COM p_end = vaddr + p_memsz + p_offset; 13348212SMichael.Corcoran@Sun.COM p_end = (caddr_t)P2ROUNDUP((size_t)p_end, PAGESIZE); 13358212SMichael.Corcoran@Sun.COM 13368212SMichael.Corcoran@Sun.COM p_align = STRUCT_FGET(mph, x.p_align); 13378212SMichael.Corcoran@Sun.COM if (p_align > 1 && p_align > max_align) { 13388212SMichael.Corcoran@Sun.COM max_align = p_align; 1339*8362SMichael.Corcoran@Sun.COM if (max_align < min_align) { 1340*8362SMichael.Corcoran@Sun.COM max_align = min_align; 1341*8362SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(min_align); 13428212SMichael.Corcoran@Sun.COM } 13438212SMichael.Corcoran@Sun.COM } 13448212SMichael.Corcoran@Sun.COM } 13458212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 13468212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + hsize)); 13478212SMichael.Corcoran@Sun.COM } 13488212SMichael.Corcoran@Sun.COM 13498212SMichael.Corcoran@Sun.COM /* 13508212SMichael.Corcoran@Sun.COM * The alignment should be a power of 2, if it isn't we forgive it 13518212SMichael.Corcoran@Sun.COM * and round up. On overflow, we'll set the alignment to max_align 13528212SMichael.Corcoran@Sun.COM * rounded down to the nearest power of 2. 13538212SMichael.Corcoran@Sun.COM */ 13548212SMichael.Corcoran@Sun.COM if (max_align > 0 && !ISP2(max_align)) { 13558212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(np2_align); 13568212SMichael.Corcoran@Sun.COM *align = 2 * (1L << (highbit(max_align) - 1)); 13578212SMichael.Corcoran@Sun.COM if (*align < max_align || 13588212SMichael.Corcoran@Sun.COM (*align > UINT_MAX && model == DATAMODEL_ILP32)) { 13598212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(np2_align_overflow); 13608212SMichael.Corcoran@Sun.COM *align = 1L << (highbit(max_align) - 1); 13618212SMichael.Corcoran@Sun.COM } 13628212SMichael.Corcoran@Sun.COM } else { 13638212SMichael.Corcoran@Sun.COM *align = max_align; 13648212SMichael.Corcoran@Sun.COM } 13658212SMichael.Corcoran@Sun.COM 1366*8362SMichael.Corcoran@Sun.COM ASSERT(*align >= PAGESIZE || *align == 0); 1367*8362SMichael.Corcoran@Sun.COM 13688212SMichael.Corcoran@Sun.COM *loadable = num_segs; 13698212SMichael.Corcoran@Sun.COM *len = p_end - start_addr; 13708212SMichael.Corcoran@Sun.COM return (0); 13718212SMichael.Corcoran@Sun.COM } 13728212SMichael.Corcoran@Sun.COM 13738212SMichael.Corcoran@Sun.COM /* 13748212SMichael.Corcoran@Sun.COM * Check the address space to see if the virtual addresses to be used are 13758212SMichael.Corcoran@Sun.COM * available. If they are not, return errno for failure. On success, 0 13768212SMichael.Corcoran@Sun.COM * will be returned, and the virtual addresses for each mmapobj_result_t 13778212SMichael.Corcoran@Sun.COM * will be reserved. Note that a reservation could have earlier been made 13788212SMichael.Corcoran@Sun.COM * for a given segment via a /dev/null mapping. If that is the case, then 13798212SMichael.Corcoran@Sun.COM * we can use that VA space for our mappings. 13808212SMichael.Corcoran@Sun.COM * Note: this function will only be used for ET_EXEC binaries. 13818212SMichael.Corcoran@Sun.COM */ 13828212SMichael.Corcoran@Sun.COM int 13838212SMichael.Corcoran@Sun.COM check_exec_addrs(int loadable, mmapobj_result_t *mrp, caddr_t start_addr) 13848212SMichael.Corcoran@Sun.COM { 13858212SMichael.Corcoran@Sun.COM int i; 13868212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 13878212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 13888212SMichael.Corcoran@Sun.COM int ret; 13898212SMichael.Corcoran@Sun.COM caddr_t myaddr; 13908212SMichael.Corcoran@Sun.COM size_t mylen; 13918212SMichael.Corcoran@Sun.COM struct seg *seg; 13928212SMichael.Corcoran@Sun.COM 13938212SMichael.Corcoran@Sun.COM /* No need to reserve swap space now since it will be reserved later */ 13948212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 13958212SMichael.Corcoran@Sun.COM as_rangelock(as); 13968212SMichael.Corcoran@Sun.COM for (i = 0; i < loadable; i++) { 13978212SMichael.Corcoran@Sun.COM 13988212SMichael.Corcoran@Sun.COM myaddr = start_addr + (size_t)mrp[i].mr_addr; 13998212SMichael.Corcoran@Sun.COM mylen = mrp[i].mr_msize; 14008212SMichael.Corcoran@Sun.COM 14018212SMichael.Corcoran@Sun.COM /* See if there is a hole in the as for this range */ 14028212SMichael.Corcoran@Sun.COM if (as_gap(as, mylen, &myaddr, &mylen, 0, NULL) == 0) { 14038212SMichael.Corcoran@Sun.COM ASSERT(myaddr == start_addr + (size_t)mrp[i].mr_addr); 14048212SMichael.Corcoran@Sun.COM ASSERT(mylen == mrp[i].mr_msize); 14058212SMichael.Corcoran@Sun.COM 14068212SMichael.Corcoran@Sun.COM #ifdef DEBUG 14078212SMichael.Corcoran@Sun.COM if (MR_GET_TYPE(mrp[i].mr_flags) == MR_PADDING) { 14088212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_padding); 14098212SMichael.Corcoran@Sun.COM } 14108212SMichael.Corcoran@Sun.COM #endif 14118212SMichael.Corcoran@Sun.COM ret = as_map(as, myaddr, mylen, segvn_create, &crargs); 14128212SMichael.Corcoran@Sun.COM if (ret) { 14138212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 14148212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i, start_addr); 14158212SMichael.Corcoran@Sun.COM return (ret); 14168212SMichael.Corcoran@Sun.COM } 14178212SMichael.Corcoran@Sun.COM } else { 14188212SMichael.Corcoran@Sun.COM /* 14198212SMichael.Corcoran@Sun.COM * There is a mapping that exists in the range 14208212SMichael.Corcoran@Sun.COM * so check to see if it was a "reservation" 14218212SMichael.Corcoran@Sun.COM * from /dev/null. The mapping is from 14228212SMichael.Corcoran@Sun.COM * /dev/null if the mapping comes from 14238212SMichael.Corcoran@Sun.COM * segdev and the type is neither MAP_SHARED 14248212SMichael.Corcoran@Sun.COM * nor MAP_PRIVATE. 14258212SMichael.Corcoran@Sun.COM */ 14268212SMichael.Corcoran@Sun.COM AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 14278212SMichael.Corcoran@Sun.COM seg = as_findseg(as, myaddr, 0); 14288212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_mapped); 14298212SMichael.Corcoran@Sun.COM if (seg && seg->s_ops == &segdev_ops && 14308212SMichael.Corcoran@Sun.COM ((SEGOP_GETTYPE(seg, myaddr) & 14318212SMichael.Corcoran@Sun.COM (MAP_SHARED | MAP_PRIVATE)) == 0) && 14328212SMichael.Corcoran@Sun.COM myaddr >= seg->s_base && 14338212SMichael.Corcoran@Sun.COM myaddr + mylen <= 14348212SMichael.Corcoran@Sun.COM seg->s_base + seg->s_size) { 14358212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_devnull); 14368212SMichael.Corcoran@Sun.COM AS_LOCK_EXIT(as, &as->a_lock); 14378212SMichael.Corcoran@Sun.COM (void) as_unmap(as, myaddr, mylen); 14388212SMichael.Corcoran@Sun.COM ret = as_map(as, myaddr, mylen, segvn_create, 14398212SMichael.Corcoran@Sun.COM &crargs); 14408212SMichael.Corcoran@Sun.COM mrp[i].mr_flags |= MR_RESV; 14418212SMichael.Corcoran@Sun.COM if (ret) { 14428212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 14438212SMichael.Corcoran@Sun.COM /* Need to remap what we unmapped */ 14448212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i + 1, 14458212SMichael.Corcoran@Sun.COM start_addr); 14468212SMichael.Corcoran@Sun.COM return (ret); 14478212SMichael.Corcoran@Sun.COM } 14488212SMichael.Corcoran@Sun.COM } else { 14498212SMichael.Corcoran@Sun.COM AS_LOCK_EXIT(as, &as->a_lock); 14508212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 14518212SMichael.Corcoran@Sun.COM mmapobj_unmap_exec(mrp, i, start_addr); 14528212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(exec_addr_in_use); 14538212SMichael.Corcoran@Sun.COM return (EADDRINUSE); 14548212SMichael.Corcoran@Sun.COM } 14558212SMichael.Corcoran@Sun.COM } 14568212SMichael.Corcoran@Sun.COM } 14578212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 14588212SMichael.Corcoran@Sun.COM return (0); 14598212SMichael.Corcoran@Sun.COM } 14608212SMichael.Corcoran@Sun.COM 14618212SMichael.Corcoran@Sun.COM /* 14628212SMichael.Corcoran@Sun.COM * Walk through the ELF program headers and extract all useful information 14638212SMichael.Corcoran@Sun.COM * for PT_LOAD and PT_SUNWBSS segments into mrp. 14648212SMichael.Corcoran@Sun.COM * Return 0 on success or error on failure. 14658212SMichael.Corcoran@Sun.COM */ 14668212SMichael.Corcoran@Sun.COM static int 14678212SMichael.Corcoran@Sun.COM process_phdr(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, mmapobj_result_t *mrp, 14688212SMichael.Corcoran@Sun.COM vnode_t *vp, uint_t *num_mapped, size_t padding, cred_t *fcred) 14698212SMichael.Corcoran@Sun.COM { 14708212SMichael.Corcoran@Sun.COM int i; 14718212SMichael.Corcoran@Sun.COM caddr_t start_addr = NULL; 14728212SMichael.Corcoran@Sun.COM caddr_t vaddr; 14738212SMichael.Corcoran@Sun.COM size_t len = 0; 14748212SMichael.Corcoran@Sun.COM size_t lib_len = 0; 14758212SMichael.Corcoran@Sun.COM int ret; 14768212SMichael.Corcoran@Sun.COM int prot; 14778212SMichael.Corcoran@Sun.COM struct lib_va *lvp = NULL; 14788212SMichael.Corcoran@Sun.COM vattr_t vattr; 14798212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 14808212SMichael.Corcoran@Sun.COM int error; 14818212SMichael.Corcoran@Sun.COM int loadable = 0; 14828212SMichael.Corcoran@Sun.COM int current = 0; 14838212SMichael.Corcoran@Sun.COM int use_lib_va = 1; 14848212SMichael.Corcoran@Sun.COM size_t align = 0; 14858212SMichael.Corcoran@Sun.COM size_t add_pad = 0; 14868212SMichael.Corcoran@Sun.COM int hdr_seen = 0; 14878212SMichael.Corcoran@Sun.COM ushort_t e_type = ehdrp->e_type; /* same offset 32 and 64 bit */ 14888212SMichael.Corcoran@Sun.COM uint_t p_type; 14898212SMichael.Corcoran@Sun.COM offset_t p_offset; 14908212SMichael.Corcoran@Sun.COM size_t p_memsz; 14918212SMichael.Corcoran@Sun.COM size_t p_filesz; 14928212SMichael.Corcoran@Sun.COM uint_t p_flags; 14938212SMichael.Corcoran@Sun.COM int hsize; 14948212SMichael.Corcoran@Sun.COM model_t model; 14958212SMichael.Corcoran@Sun.COM STRUCT_HANDLE(myphdr, mph); 14968212SMichael.Corcoran@Sun.COM 14978212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 14988212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 14998212SMichael.Corcoran@Sun.COM 15008212SMichael.Corcoran@Sun.COM /* 15018212SMichael.Corcoran@Sun.COM * Need to make sure that hsize is aligned properly. 15028212SMichael.Corcoran@Sun.COM * For 32bit processes, 4 byte alignment is required. 15038212SMichael.Corcoran@Sun.COM * For 64bit processes, 8 byte alignment is required. 15048212SMichael.Corcoran@Sun.COM * If the alignment isn't correct, we need to return failure 15058212SMichael.Corcoran@Sun.COM * since it could cause an alignment error panic while walking 15068212SMichael.Corcoran@Sun.COM * the phdr array. 15078212SMichael.Corcoran@Sun.COM */ 15088212SMichael.Corcoran@Sun.COM if (model == DATAMODEL_LP64) { 15098212SMichael.Corcoran@Sun.COM hsize = ehdrp->e_phentsize; 15108212SMichael.Corcoran@Sun.COM if (hsize & 7) { 15118212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent_align64); 15128212SMichael.Corcoran@Sun.COM return (ENOTSUP); 15138212SMichael.Corcoran@Sun.COM } 15148212SMichael.Corcoran@Sun.COM } else { 15158212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 15168212SMichael.Corcoran@Sun.COM hsize = ((Elf32_Ehdr *)ehdrp)->e_phentsize; 15178212SMichael.Corcoran@Sun.COM if (hsize & 3) { 15188212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent_align32); 15198212SMichael.Corcoran@Sun.COM return (ENOTSUP); 15208212SMichael.Corcoran@Sun.COM } 15218212SMichael.Corcoran@Sun.COM } 15228212SMichael.Corcoran@Sun.COM 15238212SMichael.Corcoran@Sun.COM if (padding != 0) { 15248212SMichael.Corcoran@Sun.COM use_lib_va = 0; 15258212SMichael.Corcoran@Sun.COM } 15268212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN) { 15278212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_FSID | AT_NODEID | AT_CTIME | AT_MTIME; 15288212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 15298212SMichael.Corcoran@Sun.COM if (error) { 15308212SMichael.Corcoran@Sun.COM return (error); 15318212SMichael.Corcoran@Sun.COM } 15328212SMichael.Corcoran@Sun.COM /* Check to see if we already have a description for this lib */ 15338212SMichael.Corcoran@Sun.COM lvp = lib_va_find(&vattr); 15348212SMichael.Corcoran@Sun.COM 15358212SMichael.Corcoran@Sun.COM if (lvp != NULL) { 15368212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_found); 15378212SMichael.Corcoran@Sun.COM if (use_lib_va) { 15388212SMichael.Corcoran@Sun.COM start_addr = mmapobj_lookup_start_addr(lvp); 15398212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 15408212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 15418212SMichael.Corcoran@Sun.COM return (ENOMEM); 15428212SMichael.Corcoran@Sun.COM } 15438212SMichael.Corcoran@Sun.COM } 15448212SMichael.Corcoran@Sun.COM 15458212SMichael.Corcoran@Sun.COM /* 15468212SMichael.Corcoran@Sun.COM * loadable may be zero if the original allocator 15478212SMichael.Corcoran@Sun.COM * of lvp hasn't finished setting it up but the rest 15488212SMichael.Corcoran@Sun.COM * of the fields will be accurate. 15498212SMichael.Corcoran@Sun.COM */ 15508212SMichael.Corcoran@Sun.COM loadable = lvp->lv_num_segs; 15518212SMichael.Corcoran@Sun.COM len = lvp->lv_len; 15528212SMichael.Corcoran@Sun.COM align = lvp->lv_align; 15538212SMichael.Corcoran@Sun.COM } 15548212SMichael.Corcoran@Sun.COM } 15558212SMichael.Corcoran@Sun.COM 15568212SMichael.Corcoran@Sun.COM /* 15578212SMichael.Corcoran@Sun.COM * Determine the span of all loadable segments and calculate the 15588212SMichael.Corcoran@Sun.COM * number of loadable segments, the total len spanned by the mappings 15598212SMichael.Corcoran@Sun.COM * and the max alignment, if we didn't get them above. 15608212SMichael.Corcoran@Sun.COM */ 15618212SMichael.Corcoran@Sun.COM if (loadable == 0) { 15628212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_loadable_yet); 15638212SMichael.Corcoran@Sun.COM ret = calc_loadable(ehdrp, phdrbase, nphdrs, &len, 15648212SMichael.Corcoran@Sun.COM &loadable, &align); 15658212SMichael.Corcoran@Sun.COM if (ret != 0) { 15668212SMichael.Corcoran@Sun.COM /* 15678212SMichael.Corcoran@Sun.COM * Since it'd be an invalid file, we shouldn't have 15688212SMichael.Corcoran@Sun.COM * cached it previously. 15698212SMichael.Corcoran@Sun.COM */ 15708212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 15718212SMichael.Corcoran@Sun.COM return (ret); 15728212SMichael.Corcoran@Sun.COM } 15738212SMichael.Corcoran@Sun.COM #ifdef DEBUG 15748212SMichael.Corcoran@Sun.COM if (lvp) { 15758212SMichael.Corcoran@Sun.COM ASSERT(len == lvp->lv_len); 15768212SMichael.Corcoran@Sun.COM ASSERT(align == lvp->lv_align); 15778212SMichael.Corcoran@Sun.COM } 15788212SMichael.Corcoran@Sun.COM #endif 15798212SMichael.Corcoran@Sun.COM } 15808212SMichael.Corcoran@Sun.COM 15818212SMichael.Corcoran@Sun.COM /* Make sure there's something to map. */ 15828212SMichael.Corcoran@Sun.COM if (len == 0 || loadable == 0) { 15838212SMichael.Corcoran@Sun.COM /* 15848212SMichael.Corcoran@Sun.COM * Since it'd be an invalid file, we shouldn't have 15858212SMichael.Corcoran@Sun.COM * cached it previously. 15868212SMichael.Corcoran@Sun.COM */ 15878212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 15888212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(nothing_to_map); 15898212SMichael.Corcoran@Sun.COM return (ENOTSUP); 15908212SMichael.Corcoran@Sun.COM } 15918212SMichael.Corcoran@Sun.COM 15928212SMichael.Corcoran@Sun.COM lib_len = len; 15938212SMichael.Corcoran@Sun.COM if (padding != 0) { 15948212SMichael.Corcoran@Sun.COM loadable += 2; 15958212SMichael.Corcoran@Sun.COM } 15968212SMichael.Corcoran@Sun.COM if (loadable > *num_mapped) { 15978212SMichael.Corcoran@Sun.COM *num_mapped = loadable; 15988212SMichael.Corcoran@Sun.COM /* cleanup previous reservation */ 15998212SMichael.Corcoran@Sun.COM if (start_addr) { 16008212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, lib_len); 16018212SMichael.Corcoran@Sun.COM } 16028212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(e2big); 16038212SMichael.Corcoran@Sun.COM if (lvp) { 16048212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 16058212SMichael.Corcoran@Sun.COM } 16068212SMichael.Corcoran@Sun.COM return (E2BIG); 16078212SMichael.Corcoran@Sun.COM } 16088212SMichael.Corcoran@Sun.COM 16098212SMichael.Corcoran@Sun.COM /* 16108212SMichael.Corcoran@Sun.COM * We now know the size of the object to map and now we need to 16118212SMichael.Corcoran@Sun.COM * get the start address to map it at. It's possible we already 16128212SMichael.Corcoran@Sun.COM * have it if we found all the info we need in the lib_va cache. 16138212SMichael.Corcoran@Sun.COM */ 16148212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN && start_addr == NULL) { 16158212SMichael.Corcoran@Sun.COM /* 16168212SMichael.Corcoran@Sun.COM * Need to make sure padding does not throw off 16178212SMichael.Corcoran@Sun.COM * required alignment. We can only specify an 16188212SMichael.Corcoran@Sun.COM * alignment for the starting address to be mapped, 16198212SMichael.Corcoran@Sun.COM * so we round padding up to the alignment and map 16208212SMichael.Corcoran@Sun.COM * from there and then throw out the extra later. 16218212SMichael.Corcoran@Sun.COM */ 16228212SMichael.Corcoran@Sun.COM if (padding != 0) { 16238212SMichael.Corcoran@Sun.COM if (align > 1) { 16248212SMichael.Corcoran@Sun.COM add_pad = P2ROUNDUP(padding, align); 16258212SMichael.Corcoran@Sun.COM len += add_pad; 16268212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(dyn_pad_align); 16278212SMichael.Corcoran@Sun.COM } else { 16288212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(dyn_pad_noalign); 16298212SMichael.Corcoran@Sun.COM len += padding; /* at beginning */ 16308212SMichael.Corcoran@Sun.COM } 16318212SMichael.Corcoran@Sun.COM len += padding; /* at end of mapping */ 16328212SMichael.Corcoran@Sun.COM } 16338212SMichael.Corcoran@Sun.COM /* 16348212SMichael.Corcoran@Sun.COM * At this point, if lvp is non-NULL, then above we 16358212SMichael.Corcoran@Sun.COM * already found it in the cache but did not get 16368212SMichael.Corcoran@Sun.COM * the start address since we were not going to use lib_va. 16378212SMichael.Corcoran@Sun.COM * Since we know that lib_va will not be used, it's safe 16388212SMichael.Corcoran@Sun.COM * to call mmapobj_alloc_start_addr and know that lvp 16398212SMichael.Corcoran@Sun.COM * will not be modified. 16408212SMichael.Corcoran@Sun.COM */ 16418212SMichael.Corcoran@Sun.COM ASSERT(lvp ? use_lib_va == 0 : 1); 16428212SMichael.Corcoran@Sun.COM start_addr = mmapobj_alloc_start_addr(&lvp, len, 16438212SMichael.Corcoran@Sun.COM use_lib_va, align, &vattr); 16448212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 16458212SMichael.Corcoran@Sun.COM if (lvp) { 16468212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 16478212SMichael.Corcoran@Sun.COM } 16488212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(alloc_start_fail); 16498212SMichael.Corcoran@Sun.COM return (ENOMEM); 16508212SMichael.Corcoran@Sun.COM } 16518212SMichael.Corcoran@Sun.COM /* 16528212SMichael.Corcoran@Sun.COM * If we can't cache it, no need to hang on to it. 16538212SMichael.Corcoran@Sun.COM * Setting lv_num_segs to non-zero will make that 16548212SMichael.Corcoran@Sun.COM * field active and since there are too many segments 16558212SMichael.Corcoran@Sun.COM * to cache, all future users will not try to use lv_mps. 16568212SMichael.Corcoran@Sun.COM */ 16578212SMichael.Corcoran@Sun.COM if (lvp != NULL && loadable > LIBVA_CACHED_SEGS && use_lib_va) { 16588212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = loadable; 16598212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 16608212SMichael.Corcoran@Sun.COM lvp = NULL; 16618212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_nocache); 16628212SMichael.Corcoran@Sun.COM } 16638212SMichael.Corcoran@Sun.COM /* 16648212SMichael.Corcoran@Sun.COM * Free the beginning of the mapping if the padding 16658212SMichael.Corcoran@Sun.COM * was not aligned correctly. 16668212SMichael.Corcoran@Sun.COM */ 16678212SMichael.Corcoran@Sun.COM if (padding != 0 && add_pad != padding) { 16688212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, 16698212SMichael.Corcoran@Sun.COM add_pad - padding); 16708212SMichael.Corcoran@Sun.COM start_addr += (add_pad - padding); 16718212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(extra_padding); 16728212SMichael.Corcoran@Sun.COM } 16738212SMichael.Corcoran@Sun.COM } 16748212SMichael.Corcoran@Sun.COM 16758212SMichael.Corcoran@Sun.COM /* 16768212SMichael.Corcoran@Sun.COM * At this point, we have reserved the virtual address space 16778212SMichael.Corcoran@Sun.COM * for our mappings. Now we need to start filling out the mrp 16788212SMichael.Corcoran@Sun.COM * array to describe all of the individual mappings we are going 16798212SMichael.Corcoran@Sun.COM * to return. 16808212SMichael.Corcoran@Sun.COM * For ET_EXEC there has been no memory reservation since we are 16818212SMichael.Corcoran@Sun.COM * using fixed addresses. While filling in the mrp array below, 16828212SMichael.Corcoran@Sun.COM * we will have the first segment biased to start at addr 0 16838212SMichael.Corcoran@Sun.COM * and the rest will be biased by this same amount. Thus if there 16848212SMichael.Corcoran@Sun.COM * is padding, the first padding will start at addr 0, and the next 16858212SMichael.Corcoran@Sun.COM * segment will start at the value of padding. 16868212SMichael.Corcoran@Sun.COM */ 16878212SMichael.Corcoran@Sun.COM 16888212SMichael.Corcoran@Sun.COM /* We'll fill out padding later, so start filling in mrp at index 1 */ 16898212SMichael.Corcoran@Sun.COM if (padding != 0) { 16908212SMichael.Corcoran@Sun.COM current = 1; 16918212SMichael.Corcoran@Sun.COM } 16928212SMichael.Corcoran@Sun.COM 16938212SMichael.Corcoran@Sun.COM /* If we have no more need for lvp let it go now */ 16948212SMichael.Corcoran@Sun.COM if (lvp != NULL && use_lib_va == 0) { 16958212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 16968212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_not_needed); 16978212SMichael.Corcoran@Sun.COM lvp = NULL; 16988212SMichael.Corcoran@Sun.COM } 16998212SMichael.Corcoran@Sun.COM 17008212SMichael.Corcoran@Sun.COM /* Now fill out the mrp structs from the program headers */ 17018212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, (struct myphdr *)phdrbase); 17028212SMichael.Corcoran@Sun.COM for (i = 0; i < nphdrs; i++) { 17038212SMichael.Corcoran@Sun.COM p_type = STRUCT_FGET(mph, x.p_type); 17048212SMichael.Corcoran@Sun.COM if (p_type == PT_LOAD || p_type == PT_SUNWBSS) { 17058212SMichael.Corcoran@Sun.COM vaddr = (caddr_t)(uintptr_t)STRUCT_FGET(mph, x.p_vaddr); 17068212SMichael.Corcoran@Sun.COM p_memsz = STRUCT_FGET(mph, x.p_memsz); 17078212SMichael.Corcoran@Sun.COM p_filesz = STRUCT_FGET(mph, x.p_filesz); 17088212SMichael.Corcoran@Sun.COM p_offset = STRUCT_FGET(mph, x.p_offset); 17098212SMichael.Corcoran@Sun.COM p_flags = STRUCT_FGET(mph, x.p_flags); 17108212SMichael.Corcoran@Sun.COM 17118212SMichael.Corcoran@Sun.COM /* 17128212SMichael.Corcoran@Sun.COM * Skip this header if it requests no memory to be 17138212SMichael.Corcoran@Sun.COM * mapped. 17148212SMichael.Corcoran@Sun.COM */ 17158212SMichael.Corcoran@Sun.COM if (p_memsz == 0) { 17168212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 17178212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 17188212SMichael.Corcoran@Sun.COM hsize)); 17198212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_mem_map_sz); 17208212SMichael.Corcoran@Sun.COM continue; 17218212SMichael.Corcoran@Sun.COM } 17228212SMichael.Corcoran@Sun.COM 17238212SMichael.Corcoran@Sun.COM prot = 0; 17248212SMichael.Corcoran@Sun.COM if (p_flags & PF_R) 17258212SMichael.Corcoran@Sun.COM prot |= PROT_READ; 17268212SMichael.Corcoran@Sun.COM if (p_flags & PF_W) 17278212SMichael.Corcoran@Sun.COM prot |= PROT_WRITE; 17288212SMichael.Corcoran@Sun.COM if (p_flags & PF_X) 17298212SMichael.Corcoran@Sun.COM prot |= PROT_EXEC; 17308212SMichael.Corcoran@Sun.COM 17318212SMichael.Corcoran@Sun.COM ASSERT(current < loadable); 17328212SMichael.Corcoran@Sun.COM mrp[current].mr_msize = p_memsz; 17338212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize = p_filesz; 17348212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = p_offset; 17358212SMichael.Corcoran@Sun.COM mrp[current].mr_prot = prot; 17368212SMichael.Corcoran@Sun.COM 17378212SMichael.Corcoran@Sun.COM if (hdr_seen == 0 && p_filesz != 0) { 17388212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = MR_HDR_ELF; 17398212SMichael.Corcoran@Sun.COM /* 17408212SMichael.Corcoran@Sun.COM * We modify mr_addr and mr_offset because we 17418212SMichael.Corcoran@Sun.COM * need to map the ELF header as well, and if 17428212SMichael.Corcoran@Sun.COM * we didn't then the header could be left out 17438212SMichael.Corcoran@Sun.COM * of the mapping that we will create later. 17448212SMichael.Corcoran@Sun.COM * Since we're removing the offset, we need to 17458212SMichael.Corcoran@Sun.COM * account for that in the other fields as well 17468212SMichael.Corcoran@Sun.COM * since we will be mapping the memory from 0 17478212SMichael.Corcoran@Sun.COM * to p_offset. 17488212SMichael.Corcoran@Sun.COM */ 17498212SMichael.Corcoran@Sun.COM if (e_type == ET_DYN) { 17508212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = 0; 17518212SMichael.Corcoran@Sun.COM mrp[current].mr_msize += p_offset; 17528212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize += p_offset; 17538212SMichael.Corcoran@Sun.COM } else { 17548212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_EXEC); 17558212SMichael.Corcoran@Sun.COM /* 17568212SMichael.Corcoran@Sun.COM * Save off the start addr which will be 17578212SMichael.Corcoran@Sun.COM * our bias for the rest of the 17588212SMichael.Corcoran@Sun.COM * ET_EXEC mappings. 17598212SMichael.Corcoran@Sun.COM */ 17608212SMichael.Corcoran@Sun.COM start_addr = vaddr - padding; 17618212SMichael.Corcoran@Sun.COM } 17628212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = (caddr_t)padding; 17638212SMichael.Corcoran@Sun.COM hdr_seen = 1; 17648212SMichael.Corcoran@Sun.COM } else { 17658212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 17668212SMichael.Corcoran@Sun.COM /* bias mr_addr */ 17678212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = 17688212SMichael.Corcoran@Sun.COM vaddr - (size_t)start_addr; 17698212SMichael.Corcoran@Sun.COM } else { 17708212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = vaddr + padding; 17718212SMichael.Corcoran@Sun.COM } 17728212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = 0; 17738212SMichael.Corcoran@Sun.COM } 17748212SMichael.Corcoran@Sun.COM current++; 17758212SMichael.Corcoran@Sun.COM } 17768212SMichael.Corcoran@Sun.COM 17778212SMichael.Corcoran@Sun.COM /* Move to next phdr */ 17788212SMichael.Corcoran@Sun.COM STRUCT_SET_HANDLE(mph, model, 17798212SMichael.Corcoran@Sun.COM (struct myphdr *)((size_t)STRUCT_BUF(mph) + 17808212SMichael.Corcoran@Sun.COM hsize)); 17818212SMichael.Corcoran@Sun.COM } 17828212SMichael.Corcoran@Sun.COM 17838212SMichael.Corcoran@Sun.COM /* Now fill out the padding segments */ 17848212SMichael.Corcoran@Sun.COM if (padding != 0) { 17858212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = NULL; 17868212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = padding; 17878212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = 0; 17888212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 17898212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = 0; 17908212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_PADDING; 17918212SMichael.Corcoran@Sun.COM 17928212SMichael.Corcoran@Sun.COM /* Setup padding for the last segment */ 17938212SMichael.Corcoran@Sun.COM ASSERT(current == loadable - 1); 17948212SMichael.Corcoran@Sun.COM mrp[current].mr_addr = (caddr_t)lib_len + padding; 17958212SMichael.Corcoran@Sun.COM mrp[current].mr_msize = padding; 17968212SMichael.Corcoran@Sun.COM mrp[current].mr_fsize = 0; 17978212SMichael.Corcoran@Sun.COM mrp[current].mr_offset = 0; 17988212SMichael.Corcoran@Sun.COM mrp[current].mr_prot = 0; 17998212SMichael.Corcoran@Sun.COM mrp[current].mr_flags = MR_PADDING; 18008212SMichael.Corcoran@Sun.COM } 18018212SMichael.Corcoran@Sun.COM 18028212SMichael.Corcoran@Sun.COM /* 18038212SMichael.Corcoran@Sun.COM * Need to make sure address ranges desired are not in use or 18048212SMichael.Corcoran@Sun.COM * are previously allocated reservations from /dev/null. For 18058212SMichael.Corcoran@Sun.COM * ET_DYN, we already made sure our address range was free. 18068212SMichael.Corcoran@Sun.COM */ 18078212SMichael.Corcoran@Sun.COM if (e_type == ET_EXEC) { 18088212SMichael.Corcoran@Sun.COM ret = check_exec_addrs(loadable, mrp, start_addr); 18098212SMichael.Corcoran@Sun.COM if (ret != 0) { 18108212SMichael.Corcoran@Sun.COM ASSERT(lvp == NULL); 18118212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(check_exec_failed); 18128212SMichael.Corcoran@Sun.COM return (ret); 18138212SMichael.Corcoran@Sun.COM } 18148212SMichael.Corcoran@Sun.COM } 18158212SMichael.Corcoran@Sun.COM 18168212SMichael.Corcoran@Sun.COM /* Finish up our business with lvp. */ 18178212SMichael.Corcoran@Sun.COM if (lvp) { 18188212SMichael.Corcoran@Sun.COM ASSERT(e_type == ET_DYN); 18198212SMichael.Corcoran@Sun.COM if (lvp->lv_num_segs == 0 && loadable <= LIBVA_CACHED_SEGS) { 18208212SMichael.Corcoran@Sun.COM bcopy(mrp, lvp->lv_mps, 18218212SMichael.Corcoran@Sun.COM loadable * sizeof (mmapobj_result_t)); 18228212SMichael.Corcoran@Sun.COM membar_producer(); 18238212SMichael.Corcoran@Sun.COM } 18248212SMichael.Corcoran@Sun.COM /* 18258212SMichael.Corcoran@Sun.COM * Setting lv_num_segs to a non-zero value indicates that 18268212SMichael.Corcoran@Sun.COM * lv_mps is now valid and can be used by other threads. 18278212SMichael.Corcoran@Sun.COM * So, the above stores need to finish before lv_num_segs 18288212SMichael.Corcoran@Sun.COM * is updated. lv_mps is only valid if lv_num_segs is 18298212SMichael.Corcoran@Sun.COM * greater than LIBVA_CACHED_SEGS. 18308212SMichael.Corcoran@Sun.COM */ 18318212SMichael.Corcoran@Sun.COM lvp->lv_num_segs = loadable; 18328212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 18338212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(lvp_used); 18348212SMichael.Corcoran@Sun.COM } 18358212SMichael.Corcoran@Sun.COM 18368212SMichael.Corcoran@Sun.COM /* Now that we have mrp completely filled out go map it */ 18378212SMichael.Corcoran@Sun.COM ret = mmapobj_map_elf(vp, start_addr, mrp, loadable, fcred, e_type); 18388212SMichael.Corcoran@Sun.COM if (ret == 0) { 18398212SMichael.Corcoran@Sun.COM *num_mapped = loadable; 18408212SMichael.Corcoran@Sun.COM } 18418212SMichael.Corcoran@Sun.COM 18428212SMichael.Corcoran@Sun.COM return (ret); 18438212SMichael.Corcoran@Sun.COM } 18448212SMichael.Corcoran@Sun.COM 18458212SMichael.Corcoran@Sun.COM /* 18468212SMichael.Corcoran@Sun.COM * Take the ELF file passed in, and do the work of mapping it. 18478212SMichael.Corcoran@Sun.COM * num_mapped in - # elements in user buffer 18488212SMichael.Corcoran@Sun.COM * num_mapped out - # sections mapped and length of mrp array if 18498212SMichael.Corcoran@Sun.COM * no errors. 18508212SMichael.Corcoran@Sun.COM */ 18518212SMichael.Corcoran@Sun.COM static int 18528212SMichael.Corcoran@Sun.COM doelfwork(Ehdr *ehdrp, vnode_t *vp, mmapobj_result_t *mrp, 18538212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 18548212SMichael.Corcoran@Sun.COM { 18558212SMichael.Corcoran@Sun.COM int error; 18568212SMichael.Corcoran@Sun.COM offset_t phoff; 18578212SMichael.Corcoran@Sun.COM int nphdrs; 18588212SMichael.Corcoran@Sun.COM unsigned char ei_class; 18598212SMichael.Corcoran@Sun.COM unsigned short phentsize; 18608212SMichael.Corcoran@Sun.COM ssize_t phsizep; 18618212SMichael.Corcoran@Sun.COM caddr_t phbasep; 18628212SMichael.Corcoran@Sun.COM int to_map; 18638212SMichael.Corcoran@Sun.COM model_t model; 18648212SMichael.Corcoran@Sun.COM 18658212SMichael.Corcoran@Sun.COM ei_class = ehdrp->e_ident[EI_CLASS]; 18668212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 18678212SMichael.Corcoran@Sun.COM if ((model == DATAMODEL_ILP32 && ei_class == ELFCLASS64) || 18688212SMichael.Corcoran@Sun.COM (model == DATAMODEL_LP64 && ei_class == ELFCLASS32)) { 18698212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(wrong_model); 18708212SMichael.Corcoran@Sun.COM return (ENOTSUP); 18718212SMichael.Corcoran@Sun.COM } 18728212SMichael.Corcoran@Sun.COM 18738212SMichael.Corcoran@Sun.COM /* Can't execute code from "noexec" mounted filesystem. */ 18748212SMichael.Corcoran@Sun.COM if (ehdrp->e_type == ET_EXEC && 18758212SMichael.Corcoran@Sun.COM (vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) { 18768212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(noexec_fs); 18778212SMichael.Corcoran@Sun.COM return (EACCES); 18788212SMichael.Corcoran@Sun.COM } 18798212SMichael.Corcoran@Sun.COM 18808212SMichael.Corcoran@Sun.COM /* 18818212SMichael.Corcoran@Sun.COM * Relocatable and core files are mapped as a single flat file 18828212SMichael.Corcoran@Sun.COM * since no interpretation is done on them by mmapobj. 18838212SMichael.Corcoran@Sun.COM */ 18848212SMichael.Corcoran@Sun.COM if (ehdrp->e_type == ET_REL || ehdrp->e_type == ET_CORE) { 18858212SMichael.Corcoran@Sun.COM to_map = padding ? 3 : 1; 18868212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 18878212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 18888212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(e2big_et_rel); 18898212SMichael.Corcoran@Sun.COM return (E2BIG); 18908212SMichael.Corcoran@Sun.COM } 18918212SMichael.Corcoran@Sun.COM error = mmapobj_map_flat(vp, mrp, padding, fcred); 18928212SMichael.Corcoran@Sun.COM if (error == 0) { 18938212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 18948212SMichael.Corcoran@Sun.COM mrp[padding ? 1 : 0].mr_flags = MR_HDR_ELF; 18958212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(et_rel_mapped); 18968212SMichael.Corcoran@Sun.COM } 18978212SMichael.Corcoran@Sun.COM return (error); 18988212SMichael.Corcoran@Sun.COM } 18998212SMichael.Corcoran@Sun.COM 19008212SMichael.Corcoran@Sun.COM /* Check for an unknown ELF type */ 19018212SMichael.Corcoran@Sun.COM if (ehdrp->e_type != ET_EXEC && ehdrp->e_type != ET_DYN) { 19028212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unknown_elf_type); 19038212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19048212SMichael.Corcoran@Sun.COM } 19058212SMichael.Corcoran@Sun.COM 19068212SMichael.Corcoran@Sun.COM if (ei_class == ELFCLASS32) { 19078212SMichael.Corcoran@Sun.COM Elf32_Ehdr *e32hdr = (Elf32_Ehdr *)ehdrp; 19088212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_ILP32); 19098212SMichael.Corcoran@Sun.COM nphdrs = e32hdr->e_phnum; 19108212SMichael.Corcoran@Sun.COM phentsize = e32hdr->e_phentsize; 19118212SMichael.Corcoran@Sun.COM if (phentsize < sizeof (Elf32_Phdr)) { 19128212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent32_too_small); 19138212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19148212SMichael.Corcoran@Sun.COM } 19158212SMichael.Corcoran@Sun.COM phoff = e32hdr->e_phoff; 19168212SMichael.Corcoran@Sun.COM } else if (ei_class == ELFCLASS64) { 19178212SMichael.Corcoran@Sun.COM Elf64_Ehdr *e64hdr = (Elf64_Ehdr *)ehdrp; 19188212SMichael.Corcoran@Sun.COM ASSERT(model == DATAMODEL_LP64); 19198212SMichael.Corcoran@Sun.COM nphdrs = e64hdr->e_phnum; 19208212SMichael.Corcoran@Sun.COM phentsize = e64hdr->e_phentsize; 19218212SMichael.Corcoran@Sun.COM if (phentsize < sizeof (Elf64_Phdr)) { 19228212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phent64_too_small); 19238212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19248212SMichael.Corcoran@Sun.COM } 19258212SMichael.Corcoran@Sun.COM phoff = e64hdr->e_phoff; 19268212SMichael.Corcoran@Sun.COM } else { 19278212SMichael.Corcoran@Sun.COM /* fallthrough case for an invalid ELF class */ 19288212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(inval_elf_class); 19298212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19308212SMichael.Corcoran@Sun.COM } 19318212SMichael.Corcoran@Sun.COM 19328212SMichael.Corcoran@Sun.COM /* 19338212SMichael.Corcoran@Sun.COM * nphdrs should only have this value for core files which are handled 19348212SMichael.Corcoran@Sun.COM * above as a single mapping. If other file types ever use this 19358212SMichael.Corcoran@Sun.COM * sentinel, then we'll add the support needed to handle this here. 19368212SMichael.Corcoran@Sun.COM */ 19378212SMichael.Corcoran@Sun.COM if (nphdrs == PN_XNUM) { 19388212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(too_many_phdrs); 19398212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19408212SMichael.Corcoran@Sun.COM } 19418212SMichael.Corcoran@Sun.COM 19428212SMichael.Corcoran@Sun.COM phsizep = nphdrs * phentsize; 19438212SMichael.Corcoran@Sun.COM 19448212SMichael.Corcoran@Sun.COM if (phsizep == 0) { 19458212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(no_phsize); 19468212SMichael.Corcoran@Sun.COM return (ENOTSUP); 19478212SMichael.Corcoran@Sun.COM } 19488212SMichael.Corcoran@Sun.COM 19498212SMichael.Corcoran@Sun.COM /* Make sure we only wait for memory if it's a reasonable request */ 19508212SMichael.Corcoran@Sun.COM if (phsizep > mmapobj_alloc_threshold) { 19518212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phsize_large); 19528212SMichael.Corcoran@Sun.COM if ((phbasep = kmem_alloc(phsizep, KM_NOSLEEP)) == NULL) { 19538212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(phsize_xtralarge); 19548212SMichael.Corcoran@Sun.COM return (ENOMEM); 19558212SMichael.Corcoran@Sun.COM } 19568212SMichael.Corcoran@Sun.COM } else { 19578212SMichael.Corcoran@Sun.COM phbasep = kmem_alloc(phsizep, KM_SLEEP); 19588212SMichael.Corcoran@Sun.COM } 19598212SMichael.Corcoran@Sun.COM 19608212SMichael.Corcoran@Sun.COM if ((error = vn_rdwr(UIO_READ, vp, phbasep, phsizep, 19618212SMichael.Corcoran@Sun.COM (offset_t)phoff, UIO_SYSSPACE, 0, (rlim64_t)0, 19628212SMichael.Corcoran@Sun.COM fcred, NULL)) != 0) { 19638212SMichael.Corcoran@Sun.COM kmem_free(phbasep, phsizep); 19648212SMichael.Corcoran@Sun.COM return (error); 19658212SMichael.Corcoran@Sun.COM } 19668212SMichael.Corcoran@Sun.COM 19678212SMichael.Corcoran@Sun.COM /* Now process the phdr's */ 19688212SMichael.Corcoran@Sun.COM error = process_phdr(ehdrp, phbasep, nphdrs, mrp, vp, num_mapped, 19698212SMichael.Corcoran@Sun.COM padding, fcred); 19708212SMichael.Corcoran@Sun.COM kmem_free(phbasep, phsizep); 19718212SMichael.Corcoran@Sun.COM return (error); 19728212SMichael.Corcoran@Sun.COM } 19738212SMichael.Corcoran@Sun.COM 19748212SMichael.Corcoran@Sun.COM #if defined(__sparc) 19758212SMichael.Corcoran@Sun.COM /* 19768212SMichael.Corcoran@Sun.COM * Hack to support 64 bit kernels running AOUT 4.x programs. 19778212SMichael.Corcoran@Sun.COM * This is the sizeof (struct nlist) for a 32 bit kernel. 19788212SMichael.Corcoran@Sun.COM * Since AOUT programs are 32 bit only, they will never use the 64 bit 19798212SMichael.Corcoran@Sun.COM * sizeof (struct nlist) and thus creating a #define is the simplest 19808212SMichael.Corcoran@Sun.COM * way around this since this is a format which is not being updated. 19818212SMichael.Corcoran@Sun.COM * This will be used in the place of sizeof (struct nlist) below. 19828212SMichael.Corcoran@Sun.COM */ 19838212SMichael.Corcoran@Sun.COM #define NLIST_SIZE (0xC) 19848212SMichael.Corcoran@Sun.COM 19858212SMichael.Corcoran@Sun.COM static int 19868212SMichael.Corcoran@Sun.COM doaoutwork(vnode_t *vp, mmapobj_result_t *mrp, 19878212SMichael.Corcoran@Sun.COM uint_t *num_mapped, struct exec *hdr, cred_t *fcred) 19888212SMichael.Corcoran@Sun.COM { 19898212SMichael.Corcoran@Sun.COM int error; 19908212SMichael.Corcoran@Sun.COM size_t size; 19918212SMichael.Corcoran@Sun.COM size_t osize; 19928212SMichael.Corcoran@Sun.COM size_t nsize; /* nlist size */ 19938212SMichael.Corcoran@Sun.COM size_t msize; 19948212SMichael.Corcoran@Sun.COM size_t zfoddiff; 19958212SMichael.Corcoran@Sun.COM caddr_t addr; 19968212SMichael.Corcoran@Sun.COM caddr_t start_addr; 19978212SMichael.Corcoran@Sun.COM struct as *as = curproc->p_as; 19988212SMichael.Corcoran@Sun.COM int prot = PROT_USER | PROT_READ | PROT_EXEC; 19998212SMichael.Corcoran@Sun.COM uint_t mflag = MAP_PRIVATE | _MAP_LOW32; 20008212SMichael.Corcoran@Sun.COM offset_t off = 0; 20018212SMichael.Corcoran@Sun.COM int segnum = 0; 20028212SMichael.Corcoran@Sun.COM uint_t to_map; 20038212SMichael.Corcoran@Sun.COM int is_library = 0; 20048212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 20058212SMichael.Corcoran@Sun.COM 20068212SMichael.Corcoran@Sun.COM /* Only 32bit apps supported by this file format */ 20078212SMichael.Corcoran@Sun.COM if (get_udatamodel() != DATAMODEL_ILP32) { 20088212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_64bit_try); 20098212SMichael.Corcoran@Sun.COM return (ENOTSUP); 20108212SMichael.Corcoran@Sun.COM } 20118212SMichael.Corcoran@Sun.COM 20128212SMichael.Corcoran@Sun.COM /* Check to see if this is a library */ 20138212SMichael.Corcoran@Sun.COM if (hdr->a_magic == ZMAGIC && hdr->a_entry < PAGESIZE) { 20148212SMichael.Corcoran@Sun.COM is_library = 1; 20158212SMichael.Corcoran@Sun.COM } 20168212SMichael.Corcoran@Sun.COM 20178212SMichael.Corcoran@Sun.COM /* Can't execute code from "noexec" mounted filesystem. */ 20188212SMichael.Corcoran@Sun.COM if (((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) && (is_library == 0)) { 20198212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_noexec); 20208212SMichael.Corcoran@Sun.COM return (EACCES); 20218212SMichael.Corcoran@Sun.COM } 20228212SMichael.Corcoran@Sun.COM 20238212SMichael.Corcoran@Sun.COM /* 20248212SMichael.Corcoran@Sun.COM * There are 2 ways to calculate the mapped size of executable: 20258212SMichael.Corcoran@Sun.COM * 1) rounded text size + data size + bss size. 20268212SMichael.Corcoran@Sun.COM * 2) starting offset for text + text size + data size + text relocation 20278212SMichael.Corcoran@Sun.COM * size + data relocation size + room for nlist data structure. 20288212SMichael.Corcoran@Sun.COM * 20298212SMichael.Corcoran@Sun.COM * The larger of the two sizes will be used to map this binary. 20308212SMichael.Corcoran@Sun.COM */ 20318212SMichael.Corcoran@Sun.COM osize = P2ROUNDUP(hdr->a_text, PAGESIZE) + hdr->a_data + hdr->a_bss; 20328212SMichael.Corcoran@Sun.COM 20338212SMichael.Corcoran@Sun.COM off = hdr->a_magic == ZMAGIC ? 0 : sizeof (struct exec); 20348212SMichael.Corcoran@Sun.COM 20358212SMichael.Corcoran@Sun.COM nsize = off + hdr->a_text + hdr->a_data + hdr->a_trsize + 20368212SMichael.Corcoran@Sun.COM hdr->a_drsize + NLIST_SIZE; 20378212SMichael.Corcoran@Sun.COM 20388212SMichael.Corcoran@Sun.COM size = MAX(osize, nsize); 20398212SMichael.Corcoran@Sun.COM if (size != nsize) { 20408212SMichael.Corcoran@Sun.COM nsize = 0; 20418212SMichael.Corcoran@Sun.COM } 20428212SMichael.Corcoran@Sun.COM 20438212SMichael.Corcoran@Sun.COM /* 20448212SMichael.Corcoran@Sun.COM * 1 seg for text and 1 seg for initialized data. 20458212SMichael.Corcoran@Sun.COM * 1 seg for bss (if can't fit in leftover space of init data) 20468212SMichael.Corcoran@Sun.COM * 1 seg for nlist if needed. 20478212SMichael.Corcoran@Sun.COM */ 20488212SMichael.Corcoran@Sun.COM to_map = 2 + (nsize ? 1 : 0) + 20498212SMichael.Corcoran@Sun.COM (hdr->a_bss > PAGESIZE - P2PHASE(hdr->a_data, PAGESIZE) ? 1 : 0); 20508212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 20518212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 20528212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_e2big); 20538212SMichael.Corcoran@Sun.COM return (E2BIG); 20548212SMichael.Corcoran@Sun.COM } 20558212SMichael.Corcoran@Sun.COM 20568212SMichael.Corcoran@Sun.COM /* Reserve address space for the whole mapping */ 20578212SMichael.Corcoran@Sun.COM if (is_library) { 20588212SMichael.Corcoran@Sun.COM /* We'll let VOP_MAP below pick our address for us */ 20598212SMichael.Corcoran@Sun.COM addr = NULL; 20608212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_lib); 20618212SMichael.Corcoran@Sun.COM } else { 20628212SMichael.Corcoran@Sun.COM /* 20638212SMichael.Corcoran@Sun.COM * default start address for fixed binaries from AOUT 4.x 20648212SMichael.Corcoran@Sun.COM * standard. 20658212SMichael.Corcoran@Sun.COM */ 20668212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_fixed); 20678212SMichael.Corcoran@Sun.COM mflag |= MAP_FIXED; 20688212SMichael.Corcoran@Sun.COM addr = (caddr_t)0x2000; 20698212SMichael.Corcoran@Sun.COM as_rangelock(as); 20708212SMichael.Corcoran@Sun.COM if (as_gap(as, size, &addr, &size, 0, NULL) != 0) { 20718212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 20728212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_addr_in_use); 20738212SMichael.Corcoran@Sun.COM return (EADDRINUSE); 20748212SMichael.Corcoran@Sun.COM } 20758212SMichael.Corcoran@Sun.COM crargs.flags |= MAP_NORESERVE; 20768212SMichael.Corcoran@Sun.COM error = as_map(as, addr, size, segvn_create, &crargs); 20778212SMichael.Corcoran@Sun.COM ASSERT(addr == (caddr_t)0x2000); 20788212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 20798212SMichael.Corcoran@Sun.COM } 20808212SMichael.Corcoran@Sun.COM 20818212SMichael.Corcoran@Sun.COM start_addr = addr; 20828212SMichael.Corcoran@Sun.COM osize = size; 20838212SMichael.Corcoran@Sun.COM 20848212SMichael.Corcoran@Sun.COM /* 20858212SMichael.Corcoran@Sun.COM * Map as large as we need, backed by file, this will be text, and 20868212SMichael.Corcoran@Sun.COM * possibly the nlist segment. We map over this mapping for bss and 20878212SMichael.Corcoran@Sun.COM * initialized data segments. 20888212SMichael.Corcoran@Sun.COM */ 20898212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, off, as, &addr, size, prot, PROT_ALL, 20908212SMichael.Corcoran@Sun.COM mflag, fcred, NULL); 20918212SMichael.Corcoran@Sun.COM if (error) { 20928212SMichael.Corcoran@Sun.COM if (!is_library) { 20938212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 20948212SMichael.Corcoran@Sun.COM } 20958212SMichael.Corcoran@Sun.COM return (error); 20968212SMichael.Corcoran@Sun.COM } 20978212SMichael.Corcoran@Sun.COM 20988212SMichael.Corcoran@Sun.COM /* pickup the value of start_addr and osize for libraries */ 20998212SMichael.Corcoran@Sun.COM start_addr = addr; 21008212SMichael.Corcoran@Sun.COM osize = size; 21018212SMichael.Corcoran@Sun.COM 21028212SMichael.Corcoran@Sun.COM /* 21038212SMichael.Corcoran@Sun.COM * We have our initial reservation/allocation so we need to use fixed 21048212SMichael.Corcoran@Sun.COM * addresses from now on. 21058212SMichael.Corcoran@Sun.COM */ 21068212SMichael.Corcoran@Sun.COM mflag |= MAP_FIXED; 21078212SMichael.Corcoran@Sun.COM 21088212SMichael.Corcoran@Sun.COM mrp[0].mr_addr = addr; 21098212SMichael.Corcoran@Sun.COM mrp[0].mr_msize = hdr->a_text; 21108212SMichael.Corcoran@Sun.COM mrp[0].mr_fsize = hdr->a_text; 21118212SMichael.Corcoran@Sun.COM mrp[0].mr_offset = 0; 21128212SMichael.Corcoran@Sun.COM mrp[0].mr_prot = PROT_READ | PROT_EXEC; 21138212SMichael.Corcoran@Sun.COM mrp[0].mr_flags = MR_HDR_AOUT; 21148212SMichael.Corcoran@Sun.COM 21158212SMichael.Corcoran@Sun.COM 21168212SMichael.Corcoran@Sun.COM /* 21178212SMichael.Corcoran@Sun.COM * Map initialized data. We are mapping over a portion of the 21188212SMichael.Corcoran@Sun.COM * previous mapping which will be unmapped in VOP_MAP below. 21198212SMichael.Corcoran@Sun.COM */ 21208212SMichael.Corcoran@Sun.COM off = P2ROUNDUP((offset_t)(hdr->a_text), PAGESIZE); 21218212SMichael.Corcoran@Sun.COM msize = off; 21228212SMichael.Corcoran@Sun.COM addr += off; 21238212SMichael.Corcoran@Sun.COM size = hdr->a_data; 21248212SMichael.Corcoran@Sun.COM error = VOP_MAP(vp, off, as, &addr, size, PROT_ALL, PROT_ALL, 21258212SMichael.Corcoran@Sun.COM mflag, fcred, NULL); 21268212SMichael.Corcoran@Sun.COM if (error) { 21278212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 21288212SMichael.Corcoran@Sun.COM return (error); 21298212SMichael.Corcoran@Sun.COM } 21308212SMichael.Corcoran@Sun.COM msize += size; 21318212SMichael.Corcoran@Sun.COM mrp[1].mr_addr = addr; 21328212SMichael.Corcoran@Sun.COM mrp[1].mr_msize = size; 21338212SMichael.Corcoran@Sun.COM mrp[1].mr_fsize = size; 21348212SMichael.Corcoran@Sun.COM mrp[1].mr_offset = 0; 21358212SMichael.Corcoran@Sun.COM mrp[1].mr_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 21368212SMichael.Corcoran@Sun.COM mrp[1].mr_flags = 0; 21378212SMichael.Corcoran@Sun.COM 21388212SMichael.Corcoran@Sun.COM /* Need to zero out remainder of page */ 21398212SMichael.Corcoran@Sun.COM addr += hdr->a_data; 21408212SMichael.Corcoran@Sun.COM zfoddiff = P2PHASE((size_t)addr, PAGESIZE); 21418212SMichael.Corcoran@Sun.COM if (zfoddiff) { 21428212SMichael.Corcoran@Sun.COM label_t ljb; 21438212SMichael.Corcoran@Sun.COM 21448212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_zfoddiff); 21458212SMichael.Corcoran@Sun.COM zfoddiff = PAGESIZE - zfoddiff; 21468212SMichael.Corcoran@Sun.COM if (on_fault(&ljb)) { 21478212SMichael.Corcoran@Sun.COM no_fault(); 21488212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_uzero_fault); 21498212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 21508212SMichael.Corcoran@Sun.COM return (EFAULT); 21518212SMichael.Corcoran@Sun.COM } 21528212SMichael.Corcoran@Sun.COM uzero(addr, zfoddiff); 21538212SMichael.Corcoran@Sun.COM no_fault(); 21548212SMichael.Corcoran@Sun.COM } 21558212SMichael.Corcoran@Sun.COM msize += zfoddiff; 21568212SMichael.Corcoran@Sun.COM segnum = 2; 21578212SMichael.Corcoran@Sun.COM 21588212SMichael.Corcoran@Sun.COM /* Map bss */ 21598212SMichael.Corcoran@Sun.COM if (hdr->a_bss > zfoddiff) { 21608212SMichael.Corcoran@Sun.COM struct segvn_crargs crargs = 21618212SMichael.Corcoran@Sun.COM SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 21628212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_map_bss); 21638212SMichael.Corcoran@Sun.COM addr += zfoddiff; 21648212SMichael.Corcoran@Sun.COM size = hdr->a_bss - zfoddiff; 21658212SMichael.Corcoran@Sun.COM as_rangelock(as); 21668212SMichael.Corcoran@Sun.COM (void) as_unmap(as, addr, size); 21678212SMichael.Corcoran@Sun.COM error = as_map(as, addr, size, segvn_create, &crargs); 21688212SMichael.Corcoran@Sun.COM as_rangeunlock(as); 21698212SMichael.Corcoran@Sun.COM msize += size; 21708212SMichael.Corcoran@Sun.COM 21718212SMichael.Corcoran@Sun.COM if (error) { 21728212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_bss_fail); 21738212SMichael.Corcoran@Sun.COM (void) as_unmap(as, start_addr, osize); 21748212SMichael.Corcoran@Sun.COM return (error); 21758212SMichael.Corcoran@Sun.COM } 21768212SMichael.Corcoran@Sun.COM mrp[2].mr_addr = addr; 21778212SMichael.Corcoran@Sun.COM mrp[2].mr_msize = size; 21788212SMichael.Corcoran@Sun.COM mrp[2].mr_fsize = 0; 21798212SMichael.Corcoran@Sun.COM mrp[2].mr_offset = 0; 21808212SMichael.Corcoran@Sun.COM mrp[2].mr_prot = PROT_READ | PROT_WRITE | PROT_EXEC; 21818212SMichael.Corcoran@Sun.COM mrp[2].mr_flags = 0; 21828212SMichael.Corcoran@Sun.COM 21838212SMichael.Corcoran@Sun.COM addr += size; 21848212SMichael.Corcoran@Sun.COM segnum = 3; 21858212SMichael.Corcoran@Sun.COM } 21868212SMichael.Corcoran@Sun.COM 21878212SMichael.Corcoran@Sun.COM /* 21888212SMichael.Corcoran@Sun.COM * If we have extra bits left over, we need to include that in how 21898212SMichael.Corcoran@Sun.COM * much we mapped to make sure the nlist logic is correct 21908212SMichael.Corcoran@Sun.COM */ 21918212SMichael.Corcoran@Sun.COM msize = P2ROUNDUP(msize, PAGESIZE); 21928212SMichael.Corcoran@Sun.COM 21938212SMichael.Corcoran@Sun.COM if (nsize && msize < nsize) { 21948212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(aout_nlist); 21958212SMichael.Corcoran@Sun.COM mrp[segnum].mr_addr = addr; 21968212SMichael.Corcoran@Sun.COM mrp[segnum].mr_msize = nsize - msize; 21978212SMichael.Corcoran@Sun.COM mrp[segnum].mr_fsize = 0; 21988212SMichael.Corcoran@Sun.COM mrp[segnum].mr_offset = 0; 21998212SMichael.Corcoran@Sun.COM mrp[segnum].mr_prot = PROT_READ | PROT_EXEC; 22008212SMichael.Corcoran@Sun.COM mrp[segnum].mr_flags = 0; 22018212SMichael.Corcoran@Sun.COM } 22028212SMichael.Corcoran@Sun.COM 22038212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 22048212SMichael.Corcoran@Sun.COM return (0); 22058212SMichael.Corcoran@Sun.COM } 22068212SMichael.Corcoran@Sun.COM #endif 22078212SMichael.Corcoran@Sun.COM 22088212SMichael.Corcoran@Sun.COM /* 22098212SMichael.Corcoran@Sun.COM * These are the two types of files that we can interpret and we want to read 22108212SMichael.Corcoran@Sun.COM * in enough info to cover both types when looking at the initial header. 22118212SMichael.Corcoran@Sun.COM */ 22128212SMichael.Corcoran@Sun.COM #define MAX_HEADER_SIZE (MAX(sizeof (Ehdr), sizeof (struct exec))) 22138212SMichael.Corcoran@Sun.COM 22148212SMichael.Corcoran@Sun.COM /* 22158212SMichael.Corcoran@Sun.COM * Map vp passed in in an interpreted manner. ELF and AOUT files will be 22168212SMichael.Corcoran@Sun.COM * interpreted and mapped appropriately for execution. 22178212SMichael.Corcoran@Sun.COM * num_mapped in - # elements in mrp 22188212SMichael.Corcoran@Sun.COM * num_mapped out - # sections mapped and length of mrp array if 22198212SMichael.Corcoran@Sun.COM * no errors or E2BIG returned. 22208212SMichael.Corcoran@Sun.COM * 22218212SMichael.Corcoran@Sun.COM * Returns 0 on success, errno value on failure. 22228212SMichael.Corcoran@Sun.COM */ 22238212SMichael.Corcoran@Sun.COM static int 22248212SMichael.Corcoran@Sun.COM mmapobj_map_interpret(vnode_t *vp, mmapobj_result_t *mrp, 22258212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 22268212SMichael.Corcoran@Sun.COM { 22278212SMichael.Corcoran@Sun.COM int error = 0; 22288212SMichael.Corcoran@Sun.COM vattr_t vattr; 22298212SMichael.Corcoran@Sun.COM struct lib_va *lvp; 22308212SMichael.Corcoran@Sun.COM caddr_t start_addr; 22318212SMichael.Corcoran@Sun.COM model_t model; 22328212SMichael.Corcoran@Sun.COM 22338212SMichael.Corcoran@Sun.COM /* 22348212SMichael.Corcoran@Sun.COM * header has to be aligned to the native size of ulong_t in order 22358212SMichael.Corcoran@Sun.COM * to avoid an unaligned access when dereferencing the header as 22368212SMichael.Corcoran@Sun.COM * a ulong_t. Thus we allocate our array on the stack of type 22378212SMichael.Corcoran@Sun.COM * ulong_t and then have header, which we dereference later as a char 22388212SMichael.Corcoran@Sun.COM * array point at lheader. 22398212SMichael.Corcoran@Sun.COM */ 22408212SMichael.Corcoran@Sun.COM ulong_t lheader[(MAX_HEADER_SIZE / (sizeof (ulong_t))) + 1]; 22418212SMichael.Corcoran@Sun.COM caddr_t header = (caddr_t)&lheader; 22428212SMichael.Corcoran@Sun.COM 22438212SMichael.Corcoran@Sun.COM vattr.va_mask = AT_FSID | AT_NODEID | AT_CTIME | AT_MTIME | AT_SIZE; 22448212SMichael.Corcoran@Sun.COM error = VOP_GETATTR(vp, &vattr, 0, fcred, NULL); 22458212SMichael.Corcoran@Sun.COM if (error) { 22468212SMichael.Corcoran@Sun.COM return (error); 22478212SMichael.Corcoran@Sun.COM } 22488212SMichael.Corcoran@Sun.COM 22498212SMichael.Corcoran@Sun.COM /* 22508212SMichael.Corcoran@Sun.COM * Check lib_va to see if we already have a full description 22518212SMichael.Corcoran@Sun.COM * for this library. This is the fast path and only used for 22528212SMichael.Corcoran@Sun.COM * ET_DYN ELF files (dynamic libraries). 22538212SMichael.Corcoran@Sun.COM */ 22548212SMichael.Corcoran@Sun.COM if (padding == 0 && (lvp = lib_va_find(&vattr)) != NULL) { 22558212SMichael.Corcoran@Sun.COM int num_segs; 22568212SMichael.Corcoran@Sun.COM 22578212SMichael.Corcoran@Sun.COM model = get_udatamodel(); 22588212SMichael.Corcoran@Sun.COM if ((model == DATAMODEL_ILP32 && 22598212SMichael.Corcoran@Sun.COM lvp->lv_flags & LV_ELF64) || 22608212SMichael.Corcoran@Sun.COM (model == DATAMODEL_LP64 && 22618212SMichael.Corcoran@Sun.COM lvp->lv_flags & LV_ELF32)) { 22628212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 22638212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_wrong_model); 22648212SMichael.Corcoran@Sun.COM return (ENOTSUP); 22658212SMichael.Corcoran@Sun.COM } 22668212SMichael.Corcoran@Sun.COM num_segs = lvp->lv_num_segs; 22678212SMichael.Corcoran@Sun.COM if (*num_mapped < num_segs) { 22688212SMichael.Corcoran@Sun.COM *num_mapped = num_segs; 22698212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 22708212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_e2big); 22718212SMichael.Corcoran@Sun.COM return (E2BIG); 22728212SMichael.Corcoran@Sun.COM } 22738212SMichael.Corcoran@Sun.COM 22748212SMichael.Corcoran@Sun.COM /* 22758212SMichael.Corcoran@Sun.COM * Check to see if we have all the mappable program headers 22768212SMichael.Corcoran@Sun.COM * cached. 22778212SMichael.Corcoran@Sun.COM */ 22788212SMichael.Corcoran@Sun.COM if (num_segs <= LIBVA_CACHED_SEGS && num_segs != 0) { 22798212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast); 22808212SMichael.Corcoran@Sun.COM start_addr = mmapobj_lookup_start_addr(lvp); 22818212SMichael.Corcoran@Sun.COM if (start_addr == NULL) { 22828212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 22838212SMichael.Corcoran@Sun.COM return (ENOMEM); 22848212SMichael.Corcoran@Sun.COM } 22858212SMichael.Corcoran@Sun.COM 22868212SMichael.Corcoran@Sun.COM bcopy(lvp->lv_mps, mrp, 22878212SMichael.Corcoran@Sun.COM num_segs * sizeof (mmapobj_result_t)); 22888212SMichael.Corcoran@Sun.COM 22898212SMichael.Corcoran@Sun.COM error = mmapobj_map_elf(vp, start_addr, mrp, 22908212SMichael.Corcoran@Sun.COM num_segs, fcred, ET_DYN); 22918212SMichael.Corcoran@Sun.COM 22928212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 22938212SMichael.Corcoran@Sun.COM if (error == 0) { 22948212SMichael.Corcoran@Sun.COM *num_mapped = num_segs; 22958212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_success); 22968212SMichael.Corcoran@Sun.COM } 22978212SMichael.Corcoran@Sun.COM return (error); 22988212SMichael.Corcoran@Sun.COM } 22998212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(fast_not_now); 23008212SMichael.Corcoran@Sun.COM 23018212SMichael.Corcoran@Sun.COM /* Release it for now since we'll look it up below */ 23028212SMichael.Corcoran@Sun.COM lib_va_release(lvp); 23038212SMichael.Corcoran@Sun.COM } 23048212SMichael.Corcoran@Sun.COM 23058212SMichael.Corcoran@Sun.COM /* 23068212SMichael.Corcoran@Sun.COM * Time to see if this is a file we can interpret. If it's smaller 23078212SMichael.Corcoran@Sun.COM * than this, then we can't interpret it. 23088212SMichael.Corcoran@Sun.COM */ 23098212SMichael.Corcoran@Sun.COM if (vattr.va_size < MAX_HEADER_SIZE) { 23108212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(small_file); 23118212SMichael.Corcoran@Sun.COM return (ENOTSUP); 23128212SMichael.Corcoran@Sun.COM } 23138212SMichael.Corcoran@Sun.COM 23148212SMichael.Corcoran@Sun.COM if ((error = vn_rdwr(UIO_READ, vp, header, MAX_HEADER_SIZE, 0, 23158212SMichael.Corcoran@Sun.COM UIO_SYSSPACE, 0, (rlim64_t)0, fcred, NULL)) != 0) { 23168212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(read_error); 23178212SMichael.Corcoran@Sun.COM return (error); 23188212SMichael.Corcoran@Sun.COM } 23198212SMichael.Corcoran@Sun.COM 23208212SMichael.Corcoran@Sun.COM /* Verify file type */ 23218212SMichael.Corcoran@Sun.COM if (header[EI_MAG0] == ELFMAG0 && header[EI_MAG1] == ELFMAG1 && 23228212SMichael.Corcoran@Sun.COM header[EI_MAG2] == ELFMAG2 && header[EI_MAG3] == ELFMAG3) { 23238212SMichael.Corcoran@Sun.COM return (doelfwork((Ehdr *)lheader, vp, mrp, num_mapped, 23248212SMichael.Corcoran@Sun.COM padding, fcred)); 23258212SMichael.Corcoran@Sun.COM } 23268212SMichael.Corcoran@Sun.COM 23278212SMichael.Corcoran@Sun.COM #if defined(__sparc) 23288212SMichael.Corcoran@Sun.COM /* On sparc, check for 4.X AOUT format */ 23298212SMichael.Corcoran@Sun.COM switch (((struct exec *)header)->a_magic) { 23308212SMichael.Corcoran@Sun.COM case OMAGIC: 23318212SMichael.Corcoran@Sun.COM case ZMAGIC: 23328212SMichael.Corcoran@Sun.COM case NMAGIC: 23338212SMichael.Corcoran@Sun.COM return (doaoutwork(vp, mrp, num_mapped, 23348212SMichael.Corcoran@Sun.COM (struct exec *)lheader, fcred)); 23358212SMichael.Corcoran@Sun.COM } 23368212SMichael.Corcoran@Sun.COM #endif 23378212SMichael.Corcoran@Sun.COM 23388212SMichael.Corcoran@Sun.COM /* Unsupported type */ 23398212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(unsupported); 23408212SMichael.Corcoran@Sun.COM return (ENOTSUP); 23418212SMichael.Corcoran@Sun.COM } 23428212SMichael.Corcoran@Sun.COM 23438212SMichael.Corcoran@Sun.COM /* 23448212SMichael.Corcoran@Sun.COM * Given a vnode, map it as either a flat file or interpret it and map 23458212SMichael.Corcoran@Sun.COM * it according to the rules of the file type. 23468212SMichael.Corcoran@Sun.COM * *num_mapped will contain the size of the mmapobj_result_t array passed in. 23478212SMichael.Corcoran@Sun.COM * If padding is non-zero, the mappings will be padded by that amount 23488212SMichael.Corcoran@Sun.COM * rounded up to the nearest pagesize. 23498212SMichael.Corcoran@Sun.COM * If the mapping is successful, *num_mapped will contain the number of 23508212SMichael.Corcoran@Sun.COM * distinct mappings created, and mrp will point to the array of 23518212SMichael.Corcoran@Sun.COM * mmapobj_result_t's which describe these mappings. 23528212SMichael.Corcoran@Sun.COM * 23538212SMichael.Corcoran@Sun.COM * On error, -1 is returned and errno is set appropriately. 23548212SMichael.Corcoran@Sun.COM * A special error case will set errno to E2BIG when there are more than 23558212SMichael.Corcoran@Sun.COM * *num_mapped mappings to be created and *num_mapped will be set to the 23568212SMichael.Corcoran@Sun.COM * number of mappings needed. 23578212SMichael.Corcoran@Sun.COM */ 23588212SMichael.Corcoran@Sun.COM int 23598212SMichael.Corcoran@Sun.COM mmapobj(vnode_t *vp, uint_t flags, mmapobj_result_t *mrp, 23608212SMichael.Corcoran@Sun.COM uint_t *num_mapped, size_t padding, cred_t *fcred) 23618212SMichael.Corcoran@Sun.COM { 23628212SMichael.Corcoran@Sun.COM int to_map; 23638212SMichael.Corcoran@Sun.COM int error = 0; 23648212SMichael.Corcoran@Sun.COM 23658212SMichael.Corcoran@Sun.COM ASSERT((padding & PAGEOFFSET) == 0); 23668212SMichael.Corcoran@Sun.COM ASSERT((flags & ~MMOBJ_ALL_FLAGS) == 0); 23678212SMichael.Corcoran@Sun.COM ASSERT(num_mapped != NULL); 23688212SMichael.Corcoran@Sun.COM ASSERT((flags & MMOBJ_PADDING) ? padding != 0 : padding == 0); 23698212SMichael.Corcoran@Sun.COM 23708212SMichael.Corcoran@Sun.COM if ((flags & MMOBJ_INTERPRET) == 0) { 23718212SMichael.Corcoran@Sun.COM to_map = padding ? 3 : 1; 23728212SMichael.Corcoran@Sun.COM if (*num_mapped < to_map) { 23738212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 23748212SMichael.Corcoran@Sun.COM MOBJ_STAT_ADD(flat_e2big); 23758212SMichael.Corcoran@Sun.COM return (E2BIG); 23768212SMichael.Corcoran@Sun.COM } 23778212SMichael.Corcoran@Sun.COM error = mmapobj_map_flat(vp, mrp, padding, fcred); 23788212SMichael.Corcoran@Sun.COM 23798212SMichael.Corcoran@Sun.COM if (error) { 23808212SMichael.Corcoran@Sun.COM return (error); 23818212SMichael.Corcoran@Sun.COM } 23828212SMichael.Corcoran@Sun.COM *num_mapped = to_map; 23838212SMichael.Corcoran@Sun.COM return (0); 23848212SMichael.Corcoran@Sun.COM } 23858212SMichael.Corcoran@Sun.COM 23868212SMichael.Corcoran@Sun.COM error = mmapobj_map_interpret(vp, mrp, num_mapped, padding, fcred); 23878212SMichael.Corcoran@Sun.COM return (error); 23888212SMichael.Corcoran@Sun.COM } 2389