1 /* Copyright (C) 2021 Free Software Foundation, Inc.
2    Contributed by Oracle.
3 
4    This file is part of GNU Binutils.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20 
21 #include "config.h"
22 #include "util.h"
23 #include "HeapMap.h"
24 
25 #define HEAPCHUNKSZ     1024 // number of HeapObj's in a chunk
26 #define HEAPCHAINS      9192 // number of address-based chains
27 #define hash(x)         (((x) >> 6) % HEAPCHAINS)
28 
29 typedef struct HeapObj
30 {
31   uint64_t addr;
32   uint64_t size;
33   long val;
34   HeapObj *next;
35 } HeapObj;
36 
37 typedef struct HeapChunk
38 {
39   void *addr;
40   HeapChunk *next;
41 } HeapChunk;
42 
HeapMap()43 HeapMap::HeapMap ()
44 {
45   chunks = NULL;
46   empty = NULL;
47   chain = new HeapObj*[HEAPCHAINS];
48   for (int i = 0; i < HEAPCHAINS; i++)
49     chain[i] = NULL;
50 
51   mmaps = new HeapObj;
52   mmaps->addr = (uint64_t) 0;
53   mmaps->size = (uint64_t) 0;
54   mmaps->val = -1;
55   mmaps->next = NULL;
56 }
57 
~HeapMap()58 HeapMap::~HeapMap ()
59 {
60   // free up all the chunks
61   HeapChunk *c = chunks;
62   while (c != NULL)
63     {
64       HeapChunk *next = c->next;
65       delete c;
66       c = next;
67     }
68   delete[] chain;
69   delete mmaps;
70 }
71 
72 void
allocate(uint64_t addr,long val)73 HeapMap::allocate (uint64_t addr, long val)
74 {
75   // get a HeapObj, and set it up for the allocated block
76   HeapObj *incoming = getHeapObj ();
77   incoming->addr = addr;
78   incoming->val = val;
79   incoming->next = NULL;
80 
81   // determine which chain the block belongs on
82   int ichain = (int) hash (addr);
83 
84   // if this is the first, just set it up and return
85   if (chain[ichain] == NULL)
86     {
87       chain[ichain] = incoming;
88       return;
89     }
90   // chain is non-empty -- find the slot for this one
91   //  chain is maintained in reverse address order
92   HeapObj *prev = NULL;
93   HeapObj *next = chain[ichain];
94 
95   for (;;)
96     {
97       if ((next == NULL) || (next->addr < incoming->addr))
98 	{
99 	  // we've found the spot
100 	  incoming->next = next;
101 	  if (prev == NULL)
102 	    chain[ichain] = incoming;
103 	  else
104 	    prev->next = incoming;
105 	  return;
106 	}
107       if (next->addr == incoming->addr)
108 	{
109 	  // error -- two blocks with same address active
110 	  //   ignore the new one
111 	  releaseHeapObj (incoming);
112 	  return;
113 	}
114       // not yet, keep looking
115       prev = next;
116       next = next->next;
117     }
118 }
119 
120 long
deallocate(uint64_t addr)121 HeapMap::deallocate (uint64_t addr)
122 {
123   int ichain = (int) hash (addr);
124   HeapObj *cur = chain[ichain];
125   HeapObj *prev = NULL;
126   while (cur != NULL)
127     {
128       if (cur->addr == addr)
129 	{
130 	  // we've found the block
131 	  long val = cur->val;
132 
133 	  // delete the block from the chain
134 	  if (prev == NULL)
135 	    chain[ichain] = cur->next;
136 	  else
137 	    prev->next = cur->next;
138 	  releaseHeapObj (cur);
139 	  return val;
140 	}
141       prev = cur;
142       cur = cur->next;
143     }
144 
145   // block not found
146   return 0;
147 }
148 
149 void
allocateChunk()150 HeapMap::allocateChunk ()
151 {
152   // allocate the memory
153   HeapChunk *c = new HeapChunk;
154   HeapObj *newc = new HeapObj[HEAPCHUNKSZ];
155 
156   // set up the chunk, and queue it for destructor's use
157   c->addr = (void *) newc;
158   c->next = chunks;
159   chunks = c;
160 
161   // Initialize the HeapObj's in the chunk to one chain
162   // last entry is left NULL, terminating the chain
163   for (int i = 0; i < (HEAPCHUNKSZ - 1); i++)
164     newc[i].next = newc + i + 1;
165   newc[HEAPCHUNKSZ - 1].next = NULL;
166 
167   // put that chain on the empty queue
168   empty = newc;
169 }
170 
171 HeapObj *
getHeapObj()172 HeapMap::getHeapObj ()
173 {
174   if (empty == NULL)
175     allocateChunk ();
176   HeapObj *ret = empty;
177   empty = ret->next;
178   return ret;
179 }
180 
181 void
releaseHeapObj(HeapObj * obj)182 HeapMap::releaseHeapObj (HeapObj *obj)
183 {
184   obj->next = empty;
185   empty = obj;
186 }
187 
188 UnmapChunk*
mmap(uint64_t addr,int64_t size,long val)189 HeapMap::mmap (uint64_t addr, int64_t size, long val)
190 {
191   HeapObj *incoming = getHeapObj ();
192   incoming->addr = addr;
193   incoming->size = size;
194   incoming->val = val;
195   incoming->next = NULL;
196   UnmapChunk* list = process (incoming, addr, size);
197   return list;
198 }
199 
200 UnmapChunk*
munmap(uint64_t addr,int64_t size)201 HeapMap::munmap (uint64_t addr, int64_t size)
202 {
203   UnmapChunk* list = process (NULL, addr, size);
204   return list;
205 }
206 
207 UnmapChunk*
process(HeapObj * obj,uint64_t addr,int64_t size)208 HeapMap::process (HeapObj *obj, uint64_t addr, int64_t size)
209 {
210   // Some graphics are used to clarify the alignment of mmap regions
211   // obj, shown as consecutive pages: "NNNNNN"
212   // cur, shown as consecutive pages: "CCCCCC"
213 
214   // Find the first overlap, start of N is before end of C.  Examples:
215   //   CCCCC
216   //     NNNNN
217   // or
218   //   CCCCC
219   //    NNN
220   // or
221   //   CCCCC
222   //  NNNNN
223   // or
224   //   CCCCC
225   //  NNNNNNN
226   HeapObj *prev = mmaps;
227   HeapObj *cur = prev->next;
228   while (cur)
229     {
230       if (addr < cur->addr + cur->size)
231 	break;
232       prev = cur;
233       cur = prev->next;
234     }
235 
236   // None found
237   if (cur == NULL)
238     {
239       prev->next = obj;
240       return NULL;
241     }
242 
243   UnmapChunk* list = NULL;
244   if (addr > cur->addr)
245     {
246       if (cur->addr + cur->size <= addr + size)
247 	{
248 	  // Process overlap on the left (low side) of new allocation
249 	  // New range: N-start to C-end (gets freed below)
250 	  prev = cur;
251 	  cur = getHeapObj ();
252 	  cur->addr = addr;
253 	  cur->size = prev->addr + prev->size - addr;
254 	  cur->val = prev->val;
255 	  cur->next = prev->next;
256 
257 	  // Truncate range: C-start to N-start
258 	  prev->size = addr - prev->addr;
259 	}
260       else
261 	{
262 	  // Process new allocation that fits completely within old allocation
263 	  // New range: N-start to N-end (freed below)
264 	  int64_t c_end = cur->addr + cur->size;
265 	  prev = cur;
266 	  cur = getHeapObj ();
267 	  cur->addr = addr;
268 	  cur->size = size;
269 	  cur->val = prev->val;
270 	  cur->next = prev->next;
271 
272 	  // Truncate range: C-start to N-start
273 	  prev->size = addr - prev->addr;
274 
275 	  // New range: N-end to C-end.
276 	  HeapObj *next = getHeapObj ();
277 	  next->addr = addr + size;
278 	  next->size = c_end - next->addr;
279 	  next->val = cur->val;
280 	  next->next = cur->next;
281 	  cur->next = next;
282 	}
283     }
284 
285   // Process all full overlaps.
286   // Copy details of cur to UnmapChunk list, remove cur from mmaps
287   while (cur && cur->addr + cur->size <= addr + size)
288     {
289 
290       UnmapChunk* uc = new UnmapChunk;
291       uc->val = cur->val;
292       uc->size = cur->size;
293       uc->next = list;
294       list = uc;
295 
296       HeapObj *t = cur;
297       cur = cur->next;
298       releaseHeapObj (t);
299     }
300 
301   if (cur && cur->addr < addr + size)
302     {
303       // Process the last overlap (right side of new allocation)
304       // Copy details of cur to UnmapChunk list
305       UnmapChunk* uc = new UnmapChunk;
306       uc->val = cur->val;
307       uc->size = addr + size - cur->addr;
308       uc->next = list;
309       list = uc;
310 
311       // Truncate left side of cur
312       cur->size -= uc->size;
313       cur->addr = addr + size;
314     }
315 
316   // Insert new allocation
317   if (obj)
318     {
319       prev->next = obj;
320       obj->next = cur;
321     }
322   else
323     prev->next = cur;
324   return list;
325 }
326