mmapobj.c revision 386e9c9ebfe4116f62e7a0950acd30564fc60125
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2014 Joyent, Inc. All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/autoconf.h>
/*
* Theory statement:
*
* The main driving force behind mmapobj is to interpret and map ELF files
* inside of the kernel instead of having the linker be responsible for this.
*
* mmapobj also supports the AOUT 4.x binary format as well as flat files in
* a read only manner.
*
* When interpreting and mapping an ELF file, mmapobj will map each PT_LOAD
* or PT_SUNWBSS segment according to the ELF standard. Refer to the "Linker
* and Libraries Guide" for more information about the standard and mapping
* rules.
*
* Having mmapobj interpret and map objects will allow the kernel to make the
* best decision for where to place the mappings for said objects. Thus, we
* can make optimizations inside of the kernel for specific platforms or
* cache mapping information to make mapping objects faster.
*
* The lib_va_hash will be one such optimization. For each ELF object that
* mmapobj is asked to interpret, we will attempt to cache the information
* about the PT_LOAD and PT_SUNWBSS sections to speed up future mappings of
* the same objects. We will cache up to LIBVA_CACHED_SEGS (see below) program
* headers which should cover a majority of the libraries out there without
* wasting space. In order to make sure that the cached information is valid,
* we check the passed in vnode's mtime and ctime to make sure the vnode
* has not been modified since the last time we used it.
*
* In addition, the lib_va_hash may contain a preferred starting VA for the
* object which can be useful for platforms which support a shared context.
* This will increase the likelyhood that library text can be shared among
* many different processes. We limit the reserved VA space for 32 bit objects
* in order to minimize fragmenting the processes address space.
*
* In addition to the above, the mmapobj interface allows for padding to be
* requested before the first mapping and after the last mapping created.
* When padding is requested, no additional optimizations will be made for
* that request.
*/
/*
* Threshold to prevent allocating too much kernel memory to read in the
* program headers for an object. If it requires more than below,
* we will use a KM_NOSLEEP allocation to allocate memory to hold all of the
* program headers which could possibly fail. If less memory than below is
* needed, then we use a KM_SLEEP allocation and are willing to wait for the
* memory if we need to.
*/
/* Debug stats for test coverage */
#ifdef DEBUG
struct mobj_stats {
#if defined(__sparc)
#endif
} mobj_stats;
#else
#define MOBJ_STAT_ADD(stat)
#endif
/*
* Check if addr is at or above the address space reserved for the stack.
* The stack is at the top of the address space for all sparc processes
* and 64 bit x86 processes. For 32 bit x86, the stack is not at the top
* of the address space and thus this check wil always return false for
* 32 bit x86 processes.
*/
#if defined(__sparc)
#define OVERLAPS_STACK(addr, p) \
#define OVERLAPS_STACK(addr, p) \
((p->p_model == DATAMODEL_LP64) && \
#define OVERLAPS_STACK(addr, p) 0
#endif
/* lv_flags values - bitmap */
/*
* Note: lv_num_segs will denote how many segments this file has and will
* only be set after the lv_mps array has been filled out.
* lv_mps can only be valid if lv_num_segs is non-zero.
*/
struct lib_va {
int lv_num_segs; /* # segs for this file */
int lv_flags;
};
#define LIB_VA_SIZE 1024
#define LIB_VA_MUTEX_SHIFT 3
#error "LIB_VA_SIZE is not a power of 2"
#endif
#define LIB_VA_HASH_MUTEX(index) \
#define LIB_VA_HASH(nodeid) \
/*
* lib_va will be used for optimized allocation of address ranges for
* libraries, such that subsequent mappings of the same library will attempt
* to use the same VA as previous mappings of that library.
* In order to map libraries at the same VA in many processes, we need to carve
* out our own address space for them which is unique across many processes.
* We use different arenas for 32 bit and 64 bit libraries.
*
* Since the 32 bit address space is relatively small, we limit the number of
* libraries which try to use consistent virtual addresses to lib_threshold.
* For 64 bit libraries there is no such limit since the address space is large.
*/
static vmem_t *lib_va_32_arena;
static vmem_t *lib_va_64_arena;
/*
* Number of 32 bit and 64 bit libraries in lib_va hash.
*/
static uint_t libs_mapped_32 = 0;
static uint_t libs_mapped_64 = 0;
/*
* Free up the resources associated with lvp as well as lvp itself.
* We also decrement the number of libraries mapped via a lib_va
* cached virtual address.
*/
void
{
if (is_64bit) {
} else {
}
}
}
/*
* See if the file associated with the vap passed in is in the lib_va hash.
* If it is and the file has not been modified since last use, then
* return a pointer to that data. Otherwise, return NULL if the file has
* changed or the file was not found in the hash.
*/
static struct lib_va *
{
} else {
/*
* file was updated since last use.
* need to remove it from list.
*/
/*
* If we can't delete it now, mark it for later
*/
}
}
if (del) {
}
return (lvp);
}
}
return (NULL);
}
/*
* Add a new entry to the lib_va hash.
* Search the hash while holding the appropriate mutex to make sure that the
* data is not already in the cache. If we find data that is in the cache
* already and has not been modified since last use, we return NULL. If it
* has been modified since last use, we will remove that entry from
* the hash and it will be deleted once it's reference count reaches zero.
* If there is no current entry in the hash we will add the new entry and
* return it to the caller who is responsible for calling lib_va_release to
* drop their reference count on it.
*
* lv_num_segs will be set to zero since the caller needs to add that
* information to the data structure.
*/
static struct lib_va *
{
model = get_udatamodel();
/*
* Make sure not adding same data a second time.
* The hash chains should be relatively short and adding
* is a relatively rare event, so it's worth the check.
*/
return (NULL);
}
/*
* We have the same nodeid and fsid but the file has
* been modified since we last saw it.
* Need to remove the old node and add this new
* one.
* Could probably use a callback mechanism to make
* this cleaner.
*/
/*
* Check to see if we can free it. If lv_refcnt
* is greater than zero, than some other thread
* has a reference to the one we want to delete
* and we can not delete it. All of this is done
* under the lib_va_hash_mutex lock so it is atomic.
*/
}
/* tmp is already advanced */
continue;
}
}
/* Caller responsible for filling this and lv_mps out */
lvp->lv_num_segs = 0;
if (model == DATAMODEL_LP64) {
} else {
}
if (model == DATAMODEL_LP64) {
} else {
}
}
if (del) {
}
return (lvp);
}
/*
* Release the hold on lvp which was acquired by lib_va_find or lib_va_add_hash.
* In addition, if this is the last hold and lvp is marked for deletion,
* free up it's reserved address space and free the structure.
*/
static void
{
int to_del = 0;
to_del = 1;
}
if (to_del) {
}
}
/*
* just returns -1.
*/
/* ARGSUSED */
static int
{
return (-1);
}
/*
* Called when an error occurred which requires mmapobj to return failure.
* reclaimed if necessary.
* num_mapped is the number of elements of mrp which have been mapped, and
* num_segs is the total number of elements in mrp.
* For e_type ET_EXEC, we need to unmap all of the elements in mrp since
* we had already made reservations for them.
* If num_mapped equals num_segs, then we know that we had fully mapped
* the file and only need to clean up the segments described.
* If they are not equal, then for ET_DYN we will unmap the range from the
* end of the last mapped segment to the end of the last segment in mrp
* since we would have made a reservation for that memory earlier.
* If e_type is passed in as zero, num_mapped must equal num_segs.
*/
void
{
int i;
}
#ifdef DEBUG
if (e_type == 0) {
}
#endif
for (i = 0; i < num_mapped; i++) {
/*
* If we are going to have to create a mapping we need to
* make sure that no one else will use the address we
* need to remap between the time it is unmapped and
* mapped below.
*/
}
/* Always need to unmap what we mapped */
struct segdev_crargs dev_a;
/*
*/
segdev_create, &dev_a);
}
}
if (num_mapped != num_segs) {
/* Need to unmap any reservation made after last mapped seg */
if (num_mapped == 0) {
} else {
}
/*
* Now we need to unmap the holes between mapped segs.
* Note that we have not mapped all of the segments and thus
* the holes between segments would not have been unmapped
* yet. If num_mapped == num_segs, then all of the holes
* between segments would have already been unmapped.
*/
for (i = 1; i < num_mapped; i++) {
}
}
}
/*
* We need to add the start address into mrp so that the unmap function
* has absolute addresses to use.
*/
static void
{
int i;
for (i = 0; i < num_mapped; i++) {
}
}
static caddr_t
{
int error;
/*
* If we don't have an expected base address, or the one that we want
* to use is not available or acceptable, go get an acceptable
* address range.
*/
ma_flags = 0;
}
if (align > 1) {
}
}
/*
* Need to reserve the address space we're going to use.
* Don't reserve swap space since we'll be mapping over this.
*/
if (error) {
}
}
return (base);
}
/*
* Get the starting address for a given file to be mapped and return it
* to the caller. If we're using lib_va and we need to allocate an address,
* we will attempt to allocate it from the global reserved pool such that the
* same address can be used in the future for this file. If we can't use the
* reserved address then we just get one that will fit in our address space.
*
* Returns the starting virtual address for the range to be mapped or NULL
* if an error is encountered. If we successfully insert the requested info
* into the lib_va hash, then *lvpp will be set to point to this lib_va
* structure. The structure will have a hold on it and thus lib_va_release
* needs to be called on it by the caller. This function will not fill out
* lv_mps or lv_num_segs since it does not have enough information to do so.
* The caller is responsible for doing this making sure that any modifications
* to lv_mps are visible before setting lv_num_segs.
*/
static caddr_t
{
int error;
model = get_udatamodel();
if (model == DATAMODEL_LP64) {
ma_flags = 0;
} else {
}
if (align > 1) {
}
if (use_lib_va) {
/*
* The first time through, we need to setup the lib_va arenas.
* We call map_addr to find a suitable range of memory to map
* the given library, and we will set the highest address
* in our vmem arena to the end of this adddress range.
* We allow up to half of the address space to be used
* for lib_va addresses but we do not prevent any allocations
* in this range from other allocation paths.
*/
if (lib_va_64_arena == NULL) {
goto nolibva;
}
/*
* Need to make sure we avoid the address hole.
* We know lib_va_end is valid but we need to
* make sure lib_va_start is as well.
*/
}
if (lib_va_64_arena == NULL) {
goto nolibva;
}
}
} else if (lib_va_32_arena == NULL &&
model == DATAMODEL_ILP32) {
if (lib_va_32_arena == NULL) {
goto nolibva;
}
if (lib_va_32_arena == NULL) {
goto nolibva;
}
}
}
}
/*
* Even if the address fails to fit in our address space,
* or we can't use a reserved address,
* we should still save it off in lib_va_hash.
*/
/*
* Check for collision on insertion and free up our VA space.
* This is expected to be rare, so we'll just reset base to
* NULL instead of looking it up in the lib_va hash.
*/
}
}
}
/*
* If we don't have an expected base address, or the one that we want
* to use is not available or acceptable, go get an acceptable
* address range.
*/
}
/*
* Need to reserve the address space we're going to use.
* Don't reserve swap space since we'll be mapping over this.
*/
/* Don't reserve swap space since we'll be mapping over this */
if (error) {
}
}
return (base);
}
/*
* Map the file associated with vp into the address space as a single
* read only private mapping.
* Returns 0 for success, and non-zero for failure to map the file.
*/
static int
{
int error = 0;
if (get_udatamodel() == DATAMODEL_LP64) {
ma_flags = 0;
}
if (error) {
return (error);
}
ma_flags |= MAP_PRIVATE;
if (padding == 0) {
if (error == 0) {
}
return (error);
}
/* padding was requested so there's more work to be done */
/* No need to reserve swap space now since it will be reserved later */
/* Need to setup padding which can only be in PAGESIZE increments. */
if (error) {
return (error);
}
start_addr = addr;
if (error == 0) {
} else {
/* Need to cleanup the as_map from earlier */
}
return (error);
}
/*
* Map a PT_LOAD or PT_SUNWBSS section of an executable file into the user's
* address space.
* vp - vnode to be mapped in
* addr - start address
* len - length of vp to be mapped
* zfodlen - length of zero filled memory after len above
* offset - offset into file where mapping should start
* prot - protections for this mapping
* fcred - credentials for the file associated with vp at open time.
*/
static int
{
int error = 0;
int full_page;
/*
* See if addr and offset are aligned such that we can map in
* full pages instead of partial pages.
*/
model = get_udatamodel();
if (len) {
int preread;
if (model == DATAMODEL_ILP32) {
mflag |= _MAP_LOW32;
}
/* We may need to map in extra bytes */
if (full_page) {
} else {
mflag |= MAP_INITDATA;
}
/*
* maxprot is passed as PROT_ALL so that mdb can
* write to this segment.
*/
return (error);
}
/*
* If the segment can fit and is relatively small, then
* we prefault the entire segment in. This is based
* on the model that says the best working set of a
* small program is all of its pages.
* We only do this if freemem will not drop below
* lotsfree since we don't want to induce paging.
*/
/*
* If we aren't prefaulting the segment,
* increment "deficit", if necessary to ensure
* that pages will become available when this
* process starts executing.
*/
}
if (preread) {
}
} else {
/*
* addr and offset were not aligned such that we could
* use VOP_MAP, thus we need to as_map the memory we
* need and then read the data in from disk.
* This code path is a corner case which should never
* be taken, but hand crafted binaries could trigger
* this logic and it needs to work correctly.
*/
/*
* We use zfod_argsp because we need to be able to
* write to the mapping and then we'll change the
* protections later if they are incorrect.
*/
if (error) {
return (error);
}
/* Now read in the data from disk */
if (error) {
return (error);
}
/*
* Now set protections.
*/
}
}
}
if (zfodlen) {
if (zfoddiff) {
/*
* Before we go to zero the remaining space on the last
* page, make sure we have write permission.
*
* We need to be careful how we zero-fill the last page
* if the protection does not include PROT_WRITE. Using
* as_setprot() can cause the VM segment code to call
* segvn_vpage(), which must allocate a page struct for
* each page in the segment. If we have a very large
* segment, this may fail, so we check for that, even
* though we ignore other return values from as_setprot.
*/
if ((prot & PROT_WRITE) == 0) {
return (ENOMEM);
}
no_fault();
if ((prot & PROT_WRITE) == 0) {
}
return (EFAULT);
}
no_fault();
/*
* Remove write protection to return to original state
*/
if ((prot & PROT_WRITE) == 0) {
}
}
struct segvn_crargs crargs =
if (error) {
return (error);
}
}
}
return (0);
}
/*
* Map the ELF file represented by vp into the users address space. The
* first mapping will start at start_addr and there will be num_elements
* mappings. The mappings are described by the data in mrp which may be
* modified upon returning from this function.
* Returns 0 for success or errno for failure.
*/
static int
{
int i;
int ret;
for (i = 0; i < num_elements; i++) {
int prot;
/* Always need to adjust mr_addr */
/* Padding has already been mapped */
continue;
}
if (ret != 0) {
return (ret);
}
/* Need to cleanup mrp to reflect the actual values used */
}
/* Also need to unmap any holes created above */
if (num_elements == 1) {
return (0);
}
return (0);
}
lo = start_addr;
/* Remove holes made by the rest of the segments */
for (i = 0; i < num_elements - 1; i++) {
/*
* If as_unmap fails we just use up a bit of extra
* space
*/
}
}
return (0);
}
/* Ugly hack to get STRUCT_* macros to work below */
struct myphdr {
Phdr x; /* native version */
};
struct myphdr32 {
Elf32_Phdr x;
};
/*
* Calculate and return the number of loadable segments in the ELF Phdr
* represented by phdrbase as well as the len of the total mapping and
* the max alignment that is needed for a given segment. On success,
* 0 is returned, and *len, *loadable and *align have been filled out.
* On failure, errno will be returned, which in this case is ENOTSUP
* if we were passed an ELF file with overlapping segments.
*/
static int
{
int i;
int hsize;
int num_segs = 0;
#if defined(__sparc)
extern int vac_size;
/*
* Want to prevent aliasing by making the start address at least be
* aligned to vac_size.
*/
#endif
model = get_udatamodel();
/* hsize alignment should have been checked before calling this func */
if (model == DATAMODEL_LP64) {
if (hsize & 7) {
return (ENOTSUP);
}
} else {
if (hsize & 3) {
return (ENOTSUP);
}
}
/*
* Determine the span of all loadable segments and calculate the
* number of loadable segments.
*/
for (i = 0; i < nphdrs; i++) {
/*
* Skip this header if it requests no memory to be
* mapped.
*/
if (p_memsz == 0) {
hsize));
continue;
}
if (num_segs++ == 0) {
/*
* The p_vaddr of the first PT_LOAD segment
* must either be NULL or within the first
* page in order to be interpreted.
* Otherwise, its an invalid file.
*/
return (ENOTSUP);
}
start_addr = vaddr;
/*
* For the first segment, we need to map from
* the beginning of the file, so we will
* adjust the size of the mapping to include
* this memory.
*/
} else {
p_offset = 0;
}
/*
* Check to make sure that this mapping wouldn't
* overlap a previous mapping.
*/
return (ENOTSUP);
}
}
}
}
}
/*
* The alignment should be a power of 2, if it isn't we forgive it
* and round up. On overflow, we'll set the alignment to max_align
* rounded down to the nearest power of 2.
*/
}
} else {
}
return (0);
}
/*
* Check the address space to see if the virtual addresses to be used are
* available. If they are not, return errno for failure. On success, 0
* will be returned, and the virtual addresses for each mmapobj_result_t
* will be reserved. Note that a reservation could have earlier been made
* we can use that VA space for our mappings.
* Note: this function will only be used for ET_EXEC binaries.
*/
int
{
int i;
int ret;
/* No need to reserve swap space now since it will be reserved later */
for (i = 0; i < loadable; i++) {
/* See if there is a hole in the as for this range */
#ifdef DEBUG
}
#endif
if (ret) {
return (ret);
}
} else {
/*
* There is a mapping that exists in the range
* so check to see if it was a "reservation"
* segdev and the type is neither MAP_SHARED
* nor MAP_PRIVATE.
*/
(MAP_SHARED | MAP_PRIVATE)) == 0) &&
&crargs);
if (ret) {
/* Need to remap what we unmapped */
return (ret);
}
} else {
return (EADDRINUSE);
}
}
}
return (0);
}
/*
* Walk through the ELF program headers and extract all useful information
* for PT_LOAD and PT_SUNWBSS segments into mrp.
* Return 0 on success or error on failure.
*/
static int
{
int i;
int ret;
int prot;
int error;
int loadable = 0;
int current = 0;
int use_lib_va = 1;
int hdr_seen = 0;
int hsize;
model = get_udatamodel();
/*
* Need to make sure that hsize is aligned properly.
* For 32bit processes, 4 byte alignment is required.
* For 64bit processes, 8 byte alignment is required.
* If the alignment isn't correct, we need to return failure
* since it could cause an alignment error panic while walking
* the phdr array.
*/
if (model == DATAMODEL_LP64) {
if (hsize & 7) {
return (ENOTSUP);
}
} else {
if (hsize & 3) {
return (ENOTSUP);
}
}
if (padding != 0) {
use_lib_va = 0;
}
if (error) {
return (error);
}
/* Check to see if we already have a description for this lib */
if (use_lib_va) {
if (start_addr == NULL) {
return (ENOMEM);
}
}
/*
* loadable may be zero if the original allocator
* of lvp hasn't finished setting it up but the rest
* of the fields will be accurate.
*/
}
}
/*
* Determine the span of all loadable segments and calculate the
* number of loadable segments, the total len spanned by the mappings
* and the max alignment, if we didn't get them above.
*/
if (loadable == 0) {
if (ret != 0) {
/*
* Since it'd be an invalid file, we shouldn't have
* cached it previously.
*/
return (ret);
}
#ifdef DEBUG
if (lvp) {
}
#endif
}
/* Make sure there's something to map. */
/*
* Since it'd be an invalid file, we shouldn't have
* cached it previously.
*/
return (ENOTSUP);
}
if (padding != 0) {
loadable += 2;
}
if (loadable > *num_mapped) {
*num_mapped = loadable;
/* cleanup previous reservation */
if (start_addr) {
}
if (lvp) {
}
return (E2BIG);
}
/*
* We now know the size of the object to map and now we need to
* get the start address to map it at. It's possible we already
* have it if we found all the info we need in the lib_va cache.
*/
/*
* Need to make sure padding does not throw off
* required alignment. We can only specify an
* alignment for the starting address to be mapped,
* so we round padding up to the alignment and map
* from there and then throw out the extra later.
*/
if (padding != 0) {
if (align > 1) {
} else {
}
}
/*
* At this point, if lvp is non-NULL, then above we
* already found it in the cache but did not get
* the start address since we were not going to use lib_va.
* Since we know that lib_va will not be used, it's safe
* to call mmapobj_alloc_start_addr and know that lvp
* will not be modified.
*/
if (start_addr == NULL) {
if (lvp) {
}
return (ENOMEM);
}
/*
* If we can't cache it, no need to hang on to it.
* Setting lv_num_segs to non-zero will make that
* field active and since there are too many segments
* to cache, all future users will not try to use lv_mps.
*/
}
/*
* Free the beginning of the mapping if the padding
* was not aligned correctly.
*/
}
}
/*
* At this point, we have reserved the virtual address space
* for our mappings. Now we need to start filling out the mrp
* array to describe all of the individual mappings we are going
* to return.
* For ET_EXEC there has been no memory reservation since we are
* using fixed addresses. While filling in the mrp array below,
* we will have the first segment biased to start at addr 0
* and the rest will be biased by this same amount. Thus if there
* is padding, the first padding will start at addr 0, and the next
* segment will start at the value of padding.
*/
/* We'll fill out padding later, so start filling in mrp at index 1 */
if (padding != 0) {
current = 1;
}
/* If we have no more need for lvp let it go now */
}
/* Now fill out the mrp structs from the program headers */
for (i = 0; i < nphdrs; i++) {
/*
* Skip this header if it requests no memory to be
* mapped.
*/
if (p_memsz == 0) {
hsize));
continue;
}
prot = 0;
prot |= PROT_WRITE;
/*
* We modify mr_offset because we
* need to map the ELF header as well, and if
* we didn't then the header could be left out
* of the mapping that we will create later.
* Since we're removing the offset, we need to
* account for that in the other fields as well
* since we will be mapping the memory from 0
* to p_offset.
*/
} else {
/*
* Save off the start addr which will be
* our bias for the rest of the
* ET_EXEC mappings.
*/
}
hdr_seen = 1;
} else {
/* bias mr_addr */
} else {
}
}
current++;
}
/* Move to next phdr */
hsize));
}
/* Now fill out the padding segments */
if (padding != 0) {
/* Setup padding for the last segment */
}
/*
* Need to make sure address ranges desired are not in use or
* ET_DYN, we already made sure our address range was free.
*/
if (ret != 0) {
return (ret);
}
}
/* Finish up our business with lvp. */
if (lvp) {
loadable * sizeof (mmapobj_result_t));
}
/*
* Setting lv_num_segs to a non-zero value indicates that
* lv_mps is now valid and can be used by other threads.
* So, the above stores need to finish before lv_num_segs
* is updated. lv_mps is only valid if lv_num_segs is
* greater than LIBVA_CACHED_SEGS.
*/
}
/* Now that we have mrp completely filled out go map it */
if (ret == 0) {
*num_mapped = loadable;
}
return (ret);
}
/*
* Take the ELF file passed in, and do the work of mapping it.
* num_mapped in - # elements in user buffer
* num_mapped out - # sections mapped and length of mrp array if
* no errors.
*/
static int
{
int error;
int nphdrs;
unsigned char ei_class;
unsigned short phentsize;
int to_map;
model = get_udatamodel();
return (ENOTSUP);
}
/* Can't execute code from "noexec" mounted filesystem. */
return (EACCES);
}
/*
* Relocatable and core files are mapped as a single flat file
* since no interpretation is done on them by mmapobj.
*/
if (*num_mapped < to_map) {
*num_mapped = to_map;
return (E2BIG);
}
if (error == 0) {
*num_mapped = to_map;
}
return (error);
}
/* Check for an unknown ELF type */
return (ENOTSUP);
}
if (ei_class == ELFCLASS32) {
if (phentsize < sizeof (Elf32_Phdr)) {
return (ENOTSUP);
}
} else if (ei_class == ELFCLASS64) {
if (phentsize < sizeof (Elf64_Phdr)) {
return (ENOTSUP);
}
} else {
/* fallthrough case for an invalid ELF class */
return (ENOTSUP);
}
/*
* nphdrs should only have this value for core files which are handled
* above as a single mapping. If other file types ever use this
* sentinel, then we'll add the support needed to handle this here.
*/
return (ENOTSUP);
}
if (phsizep == 0) {
return (ENOTSUP);
}
/* Make sure we only wait for memory if it's a reasonable request */
if (phsizep > mmapobj_alloc_threshold) {
return (ENOMEM);
}
} else {
}
return (error);
}
/* Now process the phdr's */
return (error);
}
#if defined(__sparc)
/*
* Hack to support 64 bit kernels running AOUT 4.x programs.
* This is the sizeof (struct nlist) for a 32 bit kernel.
* Since AOUT programs are 32 bit only, they will never use the 64 bit
* sizeof (struct nlist) and thus creating a #define is the simplest
* way around this since this is a format which is not being updated.
* This will be used in the place of sizeof (struct nlist) below.
*/
#define NLIST_SIZE (0xC)
static int
{
int error;
int segnum = 0;
int is_library = 0;
/* Only 32bit apps supported by this file format */
if (get_udatamodel() != DATAMODEL_ILP32) {
return (ENOTSUP);
}
/* Check to see if this is a library */
is_library = 1;
}
/* Can't execute code from "noexec" mounted filesystem. */
return (EACCES);
}
/*
* There are 2 ways to calculate the mapped size of executable:
* 1) rounded text size + data size + bss size.
* 2) starting offset for text + text size + data size + text relocation
* size + data relocation size + room for nlist data structure.
*
* The larger of the two sizes will be used to map this binary.
*/
nsize = 0;
}
/*
* 1 seg for text and 1 seg for initialized data.
* 1 seg for bss (if can't fit in leftover space of init data)
* 1 seg for nlist if needed.
*/
if (*num_mapped < to_map) {
*num_mapped = to_map;
return (E2BIG);
}
/* Reserve address space for the whole mapping */
if (is_library) {
/* We'll let VOP_MAP below pick our address for us */
} else {
/*
* default start address for fixed binaries from AOUT 4.x
* standard.
*/
return (EADDRINUSE);
}
}
start_addr = addr;
/*
* Map as large as we need, backed by file, this will be text, and
* possibly the nlist segment. We map over this mapping for bss and
* initialized data segments.
*/
if (error) {
if (!is_library) {
}
return (error);
}
/* pickup the value of start_addr and osize for libraries */
start_addr = addr;
/*
* We have our initial reservation/allocation so we need to use fixed
* addresses from now on.
*/
/*
* Map initialized data. We are mapping over a portion of the
* previous mapping which will be unmapped in VOP_MAP below.
*/
if (error) {
return (error);
}
/* Need to zero out remainder of page */
if (zfoddiff) {
no_fault();
return (EFAULT);
}
no_fault();
}
segnum = 2;
/* Map bss */
struct segvn_crargs crargs =
if (error) {
return (error);
}
segnum = 3;
}
/*
* If we have extra bits left over, we need to include that in how
* much we mapped to make sure the nlist logic is correct
*/
}
*num_mapped = to_map;
return (0);
}
#endif
/*
* These are the two types of files that we can interpret and we want to read
* in enough info to cover both types when looking at the initial header.
*/
/*
* Map vp passed in in an interpreted manner. ELF and AOUT files will be
* interpreted and mapped appropriately for execution.
* num_mapped in - # elements in mrp
* num_mapped out - # sections mapped and length of mrp array if
* no errors or E2BIG returned.
*
* Returns 0 on success, errno value on failure.
*/
static int
{
int error = 0;
/*
* header has to be aligned to the native size of ulong_t in order
* to avoid an unaligned access when dereferencing the header as
* a ulong_t. Thus we allocate our array on the stack of type
* ulong_t and then have header, which we dereference later as a char
* array point at lheader.
*/
if (error) {
return (error);
}
/*
* Check lib_va to see if we already have a full description
* for this library. This is the fast path and only used for
* ET_DYN ELF files (dynamic libraries).
*/
int num_segs;
model = get_udatamodel();
if ((model == DATAMODEL_ILP32 &&
(model == DATAMODEL_LP64 &&
return (ENOTSUP);
}
if (*num_mapped < num_segs) {
*num_mapped = num_segs;
return (E2BIG);
}
/*
* Check to see if we have all the mappable program headers
* cached.
*/
if (start_addr == NULL) {
return (ENOMEM);
}
num_segs * sizeof (mmapobj_result_t));
if (error == 0) {
*num_mapped = num_segs;
}
return (error);
}
/* Release it for now since we'll look it up below */
}
/*
* Time to see if this is a file we can interpret. If it's smaller
* than this, then we can't interpret it.
*/
return (ENOTSUP);
}
return (error);
}
/* Verify file type */
}
#if defined(__sparc)
/* On sparc, check for 4.X AOUT format */
case OMAGIC:
case ZMAGIC:
case NMAGIC:
}
#endif
/* Unsupported type */
return (ENOTSUP);
}
/*
* Given a vnode, map it as either a flat file or interpret it and map
* it according to the rules of the file type.
* *num_mapped will contain the size of the mmapobj_result_t array passed in.
* If padding is non-zero, the mappings will be padded by that amount
* rounded up to the nearest pagesize.
* If the mapping is successful, *num_mapped will contain the number of
* distinct mappings created, and mrp will point to the array of
* mmapobj_result_t's which describe these mappings.
*
* On error, -1 is returned and errno is set appropriately.
* A special error case will set errno to E2BIG when there are more than
* *num_mapped mappings to be created and *num_mapped will be set to the
* number of mappings needed.
*/
int
{
int to_map;
int error = 0;
if ((flags & MMOBJ_INTERPRET) == 0) {
if (*num_mapped < to_map) {
*num_mapped = to_map;
return (E2BIG);
}
if (error) {
return (error);
}
*num_mapped = to_map;
return (0);
}
return (error);
}