/* Copyright (C) 2020-2024 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include #include #include /* Remap pages mapped by the range [ADDR,ADDR+OLD_LEN) to new length NEW_LEN. If MREMAP_MAYMOVE is set in FLAGS the returned address may differ from ADDR. If MREMAP_FIXED is set in FLAGS the function takes another parameter which is a fixed address at which the block resides after a successful call. */ void * __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...) { error_t err; vm_address_t vm_addr = (vm_address_t) addr; vm_offset_t new_vm_addr = 0; vm_address_t begin = vm_addr; vm_address_t end; vm_size_t len; vm_prot_t prot; vm_prot_t max_prot; vm_inherit_t inherit; boolean_t shared; memory_object_name_t obj; vm_offset_t offset; if ((flags & ~(MREMAP_MAYMOVE | MREMAP_FIXED)) || ((flags & MREMAP_FIXED) && !(flags & MREMAP_MAYMOVE)) || (old_len == 0 && !(flags & MREMAP_MAYMOVE))) return (void *) (long int) __hurd_fail (EINVAL); if (flags & MREMAP_FIXED) { va_list arg; va_start (arg, flags); new_vm_addr = (vm_offset_t) va_arg (arg, void *); va_end (arg); } err = __vm_region (__mach_task_self (), &begin, &len, &prot, &max_prot, &inherit, &shared, &obj, &offset); if (err) return (void *) (uintptr_t) __hurd_fail (err); if (begin > vm_addr) { err = EFAULT; goto out; } if (begin < vm_addr || (old_len != 0 && old_len != len)) { err = EINVAL; goto out; } end = begin + len; if ((flags & MREMAP_FIXED) && ((new_vm_addr + new_len > vm_addr && new_vm_addr < end))) { /* Overlapping is not supported, like in Linux. */ err = EINVAL; goto out; } /* FIXME: locked memory. */ if (old_len != 0 && !(flags & MREMAP_FIXED)) { /* A mere change of the existing map. */ if (new_len == len) { new_vm_addr = vm_addr; goto out; } if (new_len < len) { /* Shrink. */ __mach_port_deallocate (__mach_task_self (), obj); err = __vm_deallocate (__mach_task_self (), begin + new_len, len - new_len); new_vm_addr = vm_addr; goto out; } /* Try to expand. */ err = __vm_map (__mach_task_self (), &end, new_len - len, 0, 0, obj, offset + len, 0, prot, max_prot, inherit); if (!err) { /* Ok, that worked. Now coalesce them. */ new_vm_addr = vm_addr; /* XXX this is not atomic as it is in unix! */ err = __vm_deallocate (__mach_task_self (), begin, new_len); if (err) { __vm_deallocate (__mach_task_self (), end, new_len - len); goto out; } err = __vm_map (__mach_task_self (), &begin, new_len, 0, 0, obj, offset, 0, prot, max_prot, inherit); if (err) { /* Oops, try to remap before reporting. */ __vm_map (__mach_task_self (), &begin, len, 0, 0, obj, offset, 0, prot, max_prot, inherit); } goto out; } } if (!(flags & MREMAP_MAYMOVE)) { /* Can not map here */ err = ENOMEM; goto out; } err = __vm_map (__mach_task_self (), &new_vm_addr, new_len, 0, new_vm_addr == 0, obj, offset, old_len == 0, prot, max_prot, inherit); if (err == KERN_NO_SPACE && (flags & MREMAP_FIXED)) { /* XXX this is not atomic as it is in unix! */ /* The region is already allocated; deallocate it first. */ err = __vm_deallocate (__mach_task_self (), new_vm_addr, new_len); if (! err) err = __vm_map (__mach_task_self (), &new_vm_addr, new_len, 0, 0, obj, offset, old_len == 0, prot, max_prot, inherit); } if (!err) /* Alright, can remove old mapping. */ __vm_deallocate (__mach_task_self (), begin, len); out: __mach_port_deallocate (__mach_task_self (), obj); if (err) return (void *) (uintptr_t) __hurd_fail (err); return (void *) new_vm_addr; } libc_hidden_def (__mremap) weak_alias (__mremap, mremap)