about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--sysdeps/mach/hurd/Makefile2
-rw-r--r--sysdeps/mach/hurd/Versions3
-rw-r--r--sysdeps/mach/hurd/i386/libc.abilist1
-rw-r--r--sysdeps/mach/hurd/mremap.c180
4 files changed, 185 insertions, 1 deletions
diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile
index daa69c0dae..edab284d11 100644
--- a/sysdeps/mach/hurd/Makefile
+++ b/sysdeps/mach/hurd/Makefile
@@ -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)
diff --git a/sysdeps/mach/hurd/Versions b/sysdeps/mach/hurd/Versions
index d93ad1264e..e8b4aba776 100644
--- a/sysdeps/mach/hurd/Versions
+++ b/sysdeps/mach/hurd/Versions
@@ -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;
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 60696d827f..6400885d1d 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -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
diff --git a/sysdeps/mach/hurd/mremap.c b/sysdeps/mach/hurd/mremap.c
new file mode 100644
index 0000000000..b710019539
--- /dev/null
+++ b/sysdeps/mach/hurd/mremap.c
@@ -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)