Blame sysdeps/mach/hurd/brk.c

Packit 6c4009
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <hurd.h>
Packit 6c4009
#include <hurd/resource.h>
Packit 6c4009
#include <cthreads.h>		/* For `struct mutex'.  */
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Initial maximum size of the data segment (this is arbitrary).  */
Packit 6c4009
#define	DATA_SIZE	(128 * 1024 * 1024)
Packit 6c4009
Packit 6c4009
/* Up to the page including this address is allocated from the kernel.
Packit 6c4009
   This address is the data resource limit.  */
Packit 6c4009
vm_address_t _hurd_data_end;
Packit 6c4009
Packit 6c4009
/* Up to this address is actually available to the user.
Packit 6c4009
   Pages beyond the one containing this address allow no access.  */
Packit 6c4009
vm_address_t _hurd_brk = 0;
Packit 6c4009
Packit 6c4009
/* This name is used by the Linux crtbeginS.o for reasons you don't even
Packit 6c4009
   want to think about it.  It's just easier to provide some definition for
Packit 6c4009
   it than even to explain the braindamage involved.  */
Packit 6c4009
weak_alias (_hurd_brk, ___brk_addr)
Packit 6c4009
Packit 6c4009
struct mutex _hurd_brk_lock;
Packit 6c4009
Packit 6c4009
extern int __data_start, _end;
Packit 6c4009
weak_extern (__data_start)
Packit 6c4009
static vm_address_t static_data_start;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Set the end of the process's data space to INADDR.
Packit 6c4009
   Return 0 if successful, -1 if not.  */
Packit 6c4009
int
Packit 6c4009
__brk (void *inaddr)
Packit 6c4009
{
Packit 6c4009
  int ret;
Packit 6c4009
  HURD_CRITICAL_BEGIN;
Packit 6c4009
  __mutex_lock (&_hurd_brk_lock);
Packit 6c4009
  ret = _hurd_set_brk ((vm_address_t) inaddr);
Packit 6c4009
  __mutex_unlock (&_hurd_brk_lock);
Packit 6c4009
  HURD_CRITICAL_END;
Packit 6c4009
  return ret;
Packit 6c4009
}
Packit 6c4009
weak_alias (__brk, brk)
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
_hurd_set_brk (vm_address_t addr)
Packit 6c4009
{
Packit 6c4009
  error_t err = 0;
Packit 6c4009
  vm_address_t pagend = round_page (addr);
Packit 6c4009
  vm_address_t pagebrk = round_page (_hurd_brk);
Packit 6c4009
  long int rlimit;
Packit 6c4009
Packit 6c4009
  if (pagend <= pagebrk)
Packit 6c4009
    {
Packit 6c4009
      if (pagend < pagebrk)
Packit 6c4009
	{
Packit 6c4009
	  /* XXX wish this were atomic... */
Packit 6c4009
	  /* First deallocate the memory to release its backing space.  */
Packit 6c4009
	  __vm_deallocate (__mach_task_self (), pagend, pagebrk - pagend);
Packit 6c4009
	  /* Now reallocate it with no access allowed.  */
Packit 6c4009
	  err = __vm_map (__mach_task_self (),
Packit 6c4009
			  &pagend, pagebrk - pagend,
Packit 6c4009
			  0, 0, MACH_PORT_NULL, 0, 0,
Packit 6c4009
			  0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE,
Packit 6c4009
			  VM_INHERIT_COPY);
Packit 6c4009
	  /* XXX what if error? */
Packit 6c4009
	}
Packit 6c4009
      _hurd_brk = addr;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __mutex_lock (&_hurd_rlimit_lock);
Packit 6c4009
  rlimit = _hurd_rlimits[RLIMIT_DATA].rlim_cur;
Packit 6c4009
  __mutex_unlock (&_hurd_rlimit_lock);
Packit 6c4009
Packit 6c4009
  if (addr - static_data_start > rlimit)
Packit 6c4009
    {
Packit 6c4009
      /* Need to increase the resource limit.  */
Packit 6c4009
      errno = ENOMEM;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (pagend > _hurd_data_end)
Packit 6c4009
    {
Packit 6c4009
      vm_address_t alloc_start = _hurd_data_end;
Packit 6c4009
Packit 6c4009
      /* We didn't allocate enough space!  Hopefully we can get some more!  */
Packit 6c4009
Packit 6c4009
      if (_hurd_data_end > pagebrk)
Packit 6c4009
	/* First finish allocation.  */
Packit 6c4009
	err = __vm_protect (__mach_task_self (), pagebrk,
Packit 6c4009
			    alloc_start - pagebrk, 0,
Packit 6c4009
			    VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
Packit 6c4009
      if (! err)
Packit 6c4009
	_hurd_brk = alloc_start;
Packit 6c4009
Packit 6c4009
      if (! err)
Packit 6c4009
	err = __vm_allocate (__mach_task_self (), &alloc_start,
Packit 6c4009
			     pagend - alloc_start, 0);
Packit 6c4009
Packit 6c4009
      if (! err)
Packit 6c4009
	_hurd_data_end = pagend;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* Make the memory accessible.  */
Packit 6c4009
    err = __vm_protect (__mach_task_self (), pagebrk, pagend - pagebrk,
Packit 6c4009
			0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
Packit 6c4009
Packit 6c4009
  if (err)
Packit 6c4009
    return __hurd_fail (err);
Packit 6c4009
Packit 6c4009
  _hurd_brk = addr;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
init_brk (void)
Packit 6c4009
{
Packit 6c4009
  vm_address_t pagend;
Packit 6c4009
Packit 6c4009
  __mutex_init (&_hurd_brk_lock);
Packit 6c4009
Packit 6c4009
  static_data_start = (vm_address_t) (&__data_start ?: &_end);
Packit 6c4009
Packit 6c4009
  /* If _hurd_brk is already set, don't change it.  The assumption is that
Packit 6c4009
     it was set in a previous run before something like Emacs's unexec was
Packit 6c4009
     called and dumped all the data up to the break at that point.  */
Packit 6c4009
  if (_hurd_brk == 0)
Packit 6c4009
    _hurd_brk = (vm_address_t) &_end;
Packit 6c4009
Packit 6c4009
  pagend = round_page (_hurd_brk);
Packit 6c4009
Packit 6c4009
  _hurd_data_end = round_page (static_data_start + DATA_SIZE);
Packit 6c4009
Packit 6c4009
  if (pagend < _hurd_data_end)
Packit 6c4009
    {
Packit 6c4009
      /* We use vm_map to allocate and change permissions atomically.  */
Packit 6c4009
      if (__vm_map (__mach_task_self (), &pagend, _hurd_data_end - pagend,
Packit 6c4009
		    0, 0, MACH_PORT_NULL, 0, 0,
Packit 6c4009
		    0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE,
Packit 6c4009
		    VM_INHERIT_COPY))
Packit 6c4009
	/* Couldn't allocate the memory.  The break will be very short.  */
Packit 6c4009
	_hurd_data_end = pagend;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  (void) &init_brk;		/* Avoid ``defined but not used'' warning.  */
Packit 6c4009
}
Packit 6c4009
text_set_element (_hurd_preinit_hook, init_brk);