Fixes bug in userspace's brk
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.11.1-ros / sysdeps / ros / sbrk.c
1 /* Copyright (C) 1991,1995-1997,2000,2002,2009 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <errno.h>
20 #include <stdint.h>
21 #include <unistd.h>
22 #include <bits/libc-lock.h>
23 #include <ros/memlayout.h>
24 #include <ros/procinfo.h>
25 #include <sys/mman.h>
26
27 __libc_lock_define(static,brk_lock);
28 static uintptr_t curbrk = 0;
29
30 static uintptr_t
31 __internal_getbrk (void)
32 {
33   if(curbrk == 0)
34     curbrk = (uintptr_t)__procinfo.heap_bottom;
35   return curbrk;
36 }
37
38 static int
39 __internal_setbrk (uintptr_t addr)
40 {
41   uintptr_t real_new_brk = (addr + PGSIZE - 1)/PGSIZE*PGSIZE;
42   uintptr_t real_brk = (__internal_getbrk() + PGSIZE - 1)/PGSIZE*PGSIZE;
43
44   if(real_new_brk > real_brk)
45   {
46     if(real_new_brk > BRK_END)
47       return -1;
48
49         /* Set the new curbrk on success, return -1 o/w */
50     if (mmap((void*)real_brk, real_new_brk-real_brk,
51              PROT_READ | PROT_WRITE | PROT_EXEC,
52              MAP_FIXED | MAP_ANONYMOUS, -1, 0) == (void*)real_brk) {
53                 curbrk = real_new_brk;
54                 return 0;
55         } else {
56                 return -1;
57         }
58   }
59   else if(real_new_brk < real_brk)
60   {
61     if(real_new_brk < (uintptr_t)__procinfo.heap_bottom)
62       return -1;
63
64         /* Set the new curbrk on success, return -1 o/w */
65     if (!munmap((void*)real_new_brk, real_brk - real_new_brk)) {
66                 curbrk = real_new_brk;
67                 return 0;
68         } else {
69                 return -1;
70         }
71   }
72
73   curbrk = addr;
74   return 0;
75 }
76
77 /* Set the end of the process's data space to ADDR.
78    Return 0 if successful, -1 if not.   */
79 int
80 __brk (void* addr)
81 {
82   if(addr == 0)
83     return 0;
84
85   __libc_lock_lock(brk_lock);
86   int ret = __internal_setbrk((uintptr_t)addr);
87   __libc_lock_unlock(brk_lock);
88
89   return ret;
90 }
91 weak_alias (__brk, brk)
92
93 /* Extend the process's data space by INCREMENT.
94    If INCREMENT is negative, shrink data space by - INCREMENT.
95    Return start of new space allocated, or -1 for errors.  */
96 void *
97 __sbrk (intptr_t increment)
98 {
99   __libc_lock_lock(brk_lock);
100
101   uintptr_t oldbrk = __internal_getbrk();
102   if ((increment > 0
103        ? (oldbrk + (uintptr_t) increment < oldbrk)
104        : (oldbrk < (uintptr_t) -increment))
105       || __internal_setbrk (oldbrk + increment) < 0)
106     oldbrk = -1;
107
108   __libc_lock_unlock(brk_lock);
109
110   return (void*)oldbrk;
111 }
112 libc_hidden_def (__sbrk)
113 weak_alias (__sbrk, sbrk)