mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			181 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (C) 2020-2025 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)
 |