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