| /* Copyright (C) 1991-2014 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <hurd.h> |
| #include <hurd/resource.h> |
| #include <cthreads.h> /* For `struct mutex'. */ |
| |
| |
| /* Initial maximum size of the data segment (this is arbitrary). */ |
| #define DATA_SIZE (128 * 1024 * 1024) |
| |
| /* Up to the page including this address is allocated from the kernel. |
| This address is the data resource limit. */ |
| vm_address_t _hurd_data_end; |
| |
| /* Up to this address is actually available to the user. |
| Pages beyond the one containing this address allow no access. */ |
| vm_address_t _hurd_brk = 0; |
| |
| /* This name is used by the Linux crtbeginS.o for reasons you don't even |
| want to think about it. It's just easier to provide some definition for |
| it than even to explain the braindamage involved. */ |
| weak_alias (_hurd_brk, ___brk_addr) |
| |
| struct mutex _hurd_brk_lock; |
| |
| extern int __data_start, _end; |
| weak_extern (__data_start) |
| static vm_address_t static_data_start; |
| |
| |
| /* Set the end of the process's data space to INADDR. |
| Return 0 if successful, -1 if not. */ |
| int |
| __brk (void *inaddr) |
| { |
| int ret; |
| HURD_CRITICAL_BEGIN; |
| __mutex_lock (&_hurd_brk_lock); |
| ret = _hurd_set_brk ((vm_address_t) inaddr); |
| __mutex_unlock (&_hurd_brk_lock); |
| HURD_CRITICAL_END; |
| return ret; |
| } |
| weak_alias (__brk, brk) |
| |
| |
| int |
| _hurd_set_brk (vm_address_t addr) |
| { |
| error_t err = 0; |
| vm_address_t pagend = round_page (addr); |
| vm_address_t pagebrk = round_page (_hurd_brk); |
| long int rlimit; |
| |
| if (pagend <= pagebrk) |
| { |
| if (pagend < pagebrk) |
| { |
| /* XXX wish this were atomic... */ |
| /* First deallocate the memory to release its backing space. */ |
| __vm_deallocate (__mach_task_self (), pagend, pagebrk - pagend); |
| /* Now reallocate it with no access allowed. */ |
| err = __vm_map (__mach_task_self (), |
| &pagend, pagebrk - pagend, |
| 0, 0, MACH_PORT_NULL, 0, 0, |
| 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, |
| VM_INHERIT_COPY); |
| /* XXX what if error? */ |
| } |
| _hurd_brk = addr; |
| return 0; |
| } |
| |
| __mutex_lock (&_hurd_rlimit_lock); |
| rlimit = _hurd_rlimits[RLIMIT_DATA].rlim_cur; |
| __mutex_unlock (&_hurd_rlimit_lock); |
| |
| if (addr - static_data_start > rlimit) |
| { |
| /* Need to increase the resource limit. */ |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| if (pagend > _hurd_data_end) |
| { |
| vm_address_t alloc_start = _hurd_data_end; |
| |
| /* We didn't allocate enough space! Hopefully we can get some more! */ |
| |
| if (_hurd_data_end > pagebrk) |
| /* First finish allocation. */ |
| err = __vm_protect (__mach_task_self (), pagebrk, |
| alloc_start - pagebrk, 0, |
| VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); |
| if (! err) |
| _hurd_brk = alloc_start; |
| |
| if (! err) |
| err = __vm_allocate (__mach_task_self (), &alloc_start, |
| pagend - alloc_start, 0); |
| |
| if (! err) |
| _hurd_data_end = pagend; |
| } |
| else |
| /* Make the memory accessible. */ |
| err = __vm_protect (__mach_task_self (), pagebrk, pagend - pagebrk, |
| 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); |
| |
| if (err) |
| return __hurd_fail (err); |
| |
| _hurd_brk = addr; |
| return 0; |
| } |
| |
| static void |
| init_brk (void) |
| { |
| vm_address_t pagend; |
| |
| __mutex_init (&_hurd_brk_lock); |
| |
| static_data_start = (vm_address_t) (&__data_start ?: &_end); |
| |
| /* If _hurd_brk is already set, don't change it. The assumption is that |
| it was set in a previous run before something like Emacs's unexec was |
| called and dumped all the data up to the break at that point. */ |
| if (_hurd_brk == 0) |
| _hurd_brk = (vm_address_t) &_end; |
| |
| pagend = round_page (_hurd_brk); |
| |
| _hurd_data_end = round_page (static_data_start + DATA_SIZE); |
| |
| if (pagend < _hurd_data_end) |
| { |
| /* We use vm_map to allocate and change permissions atomically. */ |
| if (__vm_map (__mach_task_self (), &pagend, _hurd_data_end - pagend, |
| 0, 0, MACH_PORT_NULL, 0, 0, |
| 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, |
| VM_INHERIT_COPY)) |
| /* Couldn't allocate the memory. The break will be very short. */ |
| _hurd_data_end = pagend; |
| } |
| |
| (void) &init_brk; /* Avoid ``defined but not used'' warning. */ |
| } |
| text_set_element (_hurd_preinit_hook, init_brk); |