@@ -201,7 +201,7 @@ sysdep_routines += f_setlk close_nocancel_nostatus read_nocancel \
endif
ifeq (misc, $(subdir))
-sysdep_routines += writev_nocancel writev_nocancel_nostatus
+sysdep_routines += writev_nocancel writev_nocancel_nostatus mremap
endif
ifeq ($(subdir),sunrpc)
@@ -7,6 +7,9 @@ libc {
# functions with a weak definition in the dynamic linker
__writev;
}
+ GLIBC_2.32 {
+ mremap;
+ }
GLIBC_PRIVATE {
# Functions shared with the dynamic linker
__access; __access_noerrno; __libc_read; __libc_write; __libc_lseek64;
@@ -2182,6 +2182,7 @@ GLIBC_2.3.4 xdr_quad_t F
GLIBC_2.3.4 xdr_u_quad_t F
GLIBC_2.30 twalk_r F
GLIBC_2.32 mach_print F
+GLIBC_2.32 mremap F
GLIBC_2.32 thrd_current F
GLIBC_2.32 thrd_equal F
GLIBC_2.32 thrd_sleep F
new file mode 100644
@@ -0,0 +1,180 @@
+/* Copyright (C) 2020 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <hurd.h>
+
+#include <stdio.h>
+
+/* 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)