| #include <errno.h> |
| #include <limits.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <unistd.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| #include <zircon/process.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/object.h> |
| |
| #include "stdio_impl.h" |
| #include "threads_impl.h" |
| #include "zircon_impl.h" |
| |
| static zx_status_t mmap_inner(uintptr_t start, size_t len, int prot, int flags, int fd, |
| off_t fd_off, void** context, uintptr_t* ptr) { |
| zx_vm_option_t zx_options = 0; |
| zx_options |= (prot & PROT_READ) ? ZX_VM_PERM_READ : 0; |
| zx_options |= (prot & PROT_WRITE) ? ZX_VM_PERM_WRITE : 0; |
| zx_options |= (prot & PROT_EXEC) ? ZX_VM_PERM_EXECUTE : 0; |
| |
| size_t offset; |
| if (flags & MAP_FIXED) { |
| zx_options |= ZX_VM_SPECIFIC_OVERWRITE; |
| zx_info_vmar_t info; |
| zx_status_t status = |
| _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info, sizeof(info), NULL, NULL); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (start < info.base) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| offset = start - info.base; |
| } else { |
| offset = 0; |
| } |
| |
| // Either create a new VMO if this is an anonymous mapping, or obtain one from the backing fd. |
| zx_handle_t vmo; |
| if (flags & MAP_ANON) { |
| zx_status_t status = _zx_vmo_create(len, 0, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| static const char kMmapAnonymousVmoName[] = "mmap-anonymous"; |
| { |
| zx_status_t status = _zx_object_set_property(vmo, ZX_PROP_NAME, kMmapAnonymousVmoName, |
| (sizeof(kMmapAnonymousVmoName)) - 1); |
| ZX_ASSERT_MSG(status == ZX_OK, "failed to set_property(ZX_PROP_NAME): %s", |
| _zx_status_get_string(status)); |
| } |
| if (flags & MAP_JIT) { |
| zx_status_t status = _zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| } else { |
| *context = _fd_get_context(fd); |
| if (*context == NULL) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zx_options |= ZX_VM_ALLOW_FAULTS; |
| zx_status_t status = _mmap_get_vmo_from_context(prot, flags, *context, &vmo); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| |
| // Map the VMO with the specified options. |
| zx_status_t status = |
| _zx_vmar_map(_zx_vmar_root_self(), zx_options, offset, vmo, fd_off, len, ptr); |
| // The VMAR keeps an internal handle to the mapped VMO, so we can close the existing handle. |
| { |
| zx_status_t status = _zx_handle_close(vmo); |
| ZX_ASSERT_MSG(status == ZX_OK, "failed to handle_close(): %s", _zx_status_get_string(status)); |
| } |
| return status; |
| } |
| |
| // mmap implementation |
| void* __mmap(void* start, size_t len, int prot, int flags, int fd, off_t fd_off) { |
| if (fd_off & (PAGE_SIZE - 1)) { |
| errno = EINVAL; |
| return MAP_FAILED; |
| } |
| if (len == 0) { |
| errno = EINVAL; |
| return MAP_FAILED; |
| } |
| if (len >= PTRDIFF_MAX) { |
| errno = ENOMEM; |
| return MAP_FAILED; |
| } |
| if (!(flags & (MAP_PRIVATE | MAP_SHARED)) || (flags & MAP_PRIVATE && flags & MAP_SHARED)) { |
| errno = EINVAL; |
| return MAP_FAILED; |
| } |
| |
| // The POSIX standard requires the file be opened with read permission regardless of the specified |
| // PROT_* flags. Implementations are permitted to provide access types exceeding those requested |
| // (e.g. PROT_WRITE may imply PROT_READ as well). Since zx_vmar_map currently disallows mapping |
| // writable/executable VMOs without read rights, we must set PROT_READ if either PROT_WRITE or |
| // PROT_EXEC is specified. |
| // https://cs.opensource.google/fuchsia/fuchsia/+/5cbdfbb2a7be562a76095a384efca39dac00d477:zircon/kernel/object/vm_address_region_dispatcher.cc;l=260 |
| prot |= (prot & (PROT_WRITE | PROT_EXEC)) ? PROT_READ : 0; |
| |
| // round up to page size |
| len = (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); |
| |
| void* context = NULL; |
| uintptr_t ptr_val; |
| zx_status_t status = |
| mmap_inner((uintptr_t)start, len, prot, flags, fd, fd_off, &context, &ptr_val); |
| void* ptr = (void*)ptr_val; |
| if (context != NULL) { |
| if (status == ZX_OK) { |
| status = _mmap_on_mapped(context, ptr); |
| if (status != ZX_OK) { |
| zx_status_t unmap_status = _zx_vmar_unmap(_zx_vmar_root_self(), ptr_val, len); |
| ZX_ASSERT_MSG(unmap_status == ZX_OK, "failed to vmar_unmap(): %s", _zx_status_get_string(unmap_status)); |
| } |
| } |
| _fd_release_context(context); |
| } |
| switch (status) { |
| case ZX_OK: |
| return ptr; |
| case ZX_ERR_BAD_HANDLE: |
| errno = EBADF; |
| break; |
| case ZX_ERR_NOT_SUPPORTED: |
| errno = ENODEV; |
| break; |
| case ZX_ERR_ACCESS_DENIED: |
| errno = EACCES; |
| break; |
| case ZX_ERR_NO_MEMORY: |
| errno = ENOMEM; |
| break; |
| case ZX_ERR_INVALID_ARGS: |
| case ZX_ERR_BAD_STATE: |
| default: |
| errno = EINVAL; |
| break; |
| } |
| return MAP_FAILED; |
| } |
| |
| weak_alias(__mmap, mmap); |