| /* Copyright (C) 2021-2023 Free Software Foundation, Inc. | 
 |    Contributed by Oracle. | 
 |  | 
 |    This file is part of GNU Binutils. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3, or (at your option) | 
 |    any later version. | 
 |  | 
 |    This program 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 General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program; if not, write to the Free Software | 
 |    Foundation, 51 Franklin Street - Fifth Floor, Boston, | 
 |    MA 02110-1301, USA.  */ | 
 |  | 
 | #include "config.h" | 
 | #include "util.h" | 
 | #include "HeapMap.h" | 
 |  | 
 | #define HEAPCHUNKSZ     1024 // number of HeapObj's in a chunk | 
 | #define HEAPCHAINS      9192 // number of address-based chains | 
 | #define hash(x)         (((x) >> 6) % HEAPCHAINS) | 
 |  | 
 | typedef struct HeapObj | 
 | { | 
 |   uint64_t addr; | 
 |   uint64_t size; | 
 |   long val; | 
 |   HeapObj *next; | 
 | } HeapObj; | 
 |  | 
 | typedef struct HeapChunk | 
 | { | 
 |   void *addr; | 
 |   HeapChunk *next; | 
 | } HeapChunk; | 
 |  | 
 | HeapMap::HeapMap () | 
 | { | 
 |   chunks = NULL; | 
 |   empty = NULL; | 
 |   chain = new HeapObj*[HEAPCHAINS]; | 
 |   for (int i = 0; i < HEAPCHAINS; i++) | 
 |     chain[i] = NULL; | 
 |  | 
 |   mmaps = new HeapObj; | 
 |   mmaps->addr = (uint64_t) 0; | 
 |   mmaps->size = (uint64_t) 0; | 
 |   mmaps->val = -1; | 
 |   mmaps->next = NULL; | 
 | } | 
 |  | 
 | HeapMap::~HeapMap () | 
 | { | 
 |   // free up all the chunks | 
 |   HeapChunk *c = chunks; | 
 |   while (c != NULL) | 
 |     { | 
 |       HeapChunk *next = c->next; | 
 |       delete c; | 
 |       c = next; | 
 |     } | 
 |   delete[] chain; | 
 |   delete mmaps; | 
 | } | 
 |  | 
 | void | 
 | HeapMap::allocate (uint64_t addr, long val) | 
 | { | 
 |   // get a HeapObj, and set it up for the allocated block | 
 |   HeapObj *incoming = getHeapObj (); | 
 |   incoming->addr = addr; | 
 |   incoming->val = val; | 
 |   incoming->next = NULL; | 
 |  | 
 |   // determine which chain the block belongs on | 
 |   int ichain = (int) hash (addr); | 
 |  | 
 |   // if this is the first, just set it up and return | 
 |   if (chain[ichain] == NULL) | 
 |     { | 
 |       chain[ichain] = incoming; | 
 |       return; | 
 |     } | 
 |   // chain is non-empty -- find the slot for this one | 
 |   //  chain is maintained in reverse address order | 
 |   HeapObj *prev = NULL; | 
 |   HeapObj *next = chain[ichain]; | 
 |  | 
 |   for (;;) | 
 |     { | 
 |       if ((next == NULL) || (next->addr < incoming->addr)) | 
 | 	{ | 
 | 	  // we've found the spot | 
 | 	  incoming->next = next; | 
 | 	  if (prev == NULL) | 
 | 	    chain[ichain] = incoming; | 
 | 	  else | 
 | 	    prev->next = incoming; | 
 | 	  return; | 
 | 	} | 
 |       if (next->addr == incoming->addr) | 
 | 	{ | 
 | 	  // error -- two blocks with same address active | 
 | 	  //   ignore the new one | 
 | 	  releaseHeapObj (incoming); | 
 | 	  return; | 
 | 	} | 
 |       // not yet, keep looking | 
 |       prev = next; | 
 |       next = next->next; | 
 |     } | 
 | } | 
 |  | 
 | long | 
 | HeapMap::deallocate (uint64_t addr) | 
 | { | 
 |   int ichain = (int) hash (addr); | 
 |   HeapObj *cur = chain[ichain]; | 
 |   HeapObj *prev = NULL; | 
 |   while (cur != NULL) | 
 |     { | 
 |       if (cur->addr == addr) | 
 | 	{ | 
 | 	  // we've found the block | 
 | 	  long val = cur->val; | 
 |  | 
 | 	  // delete the block from the chain | 
 | 	  if (prev == NULL) | 
 | 	    chain[ichain] = cur->next; | 
 | 	  else | 
 | 	    prev->next = cur->next; | 
 | 	  releaseHeapObj (cur); | 
 | 	  return val; | 
 | 	} | 
 |       prev = cur; | 
 |       cur = cur->next; | 
 |     } | 
 |  | 
 |   // block not found | 
 |   return 0; | 
 | } | 
 |  | 
 | void | 
 | HeapMap::allocateChunk () | 
 | { | 
 |   // allocate the memory | 
 |   HeapChunk *c = new HeapChunk; | 
 |   HeapObj *newc = new HeapObj[HEAPCHUNKSZ]; | 
 |  | 
 |   // set up the chunk, and queue it for destructor's use | 
 |   c->addr = (void *) newc; | 
 |   c->next = chunks; | 
 |   chunks = c; | 
 |  | 
 |   // Initialize the HeapObj's in the chunk to one chain | 
 |   // last entry is left NULL, terminating the chain | 
 |   for (int i = 0; i < (HEAPCHUNKSZ - 1); i++) | 
 |     newc[i].next = newc + i + 1; | 
 |   newc[HEAPCHUNKSZ - 1].next = NULL; | 
 |  | 
 |   // put that chain on the empty queue | 
 |   empty = newc; | 
 | } | 
 |  | 
 | HeapObj * | 
 | HeapMap::getHeapObj () | 
 | { | 
 |   if (empty == NULL) | 
 |     allocateChunk (); | 
 |   HeapObj *ret = empty; | 
 |   empty = ret->next; | 
 |   return ret; | 
 | } | 
 |  | 
 | void | 
 | HeapMap::releaseHeapObj (HeapObj *obj) | 
 | { | 
 |   obj->next = empty; | 
 |   empty = obj; | 
 | } | 
 |  | 
 | UnmapChunk* | 
 | HeapMap::mmap (uint64_t addr, int64_t size, long val) | 
 | { | 
 |   HeapObj *incoming = getHeapObj (); | 
 |   incoming->addr = addr; | 
 |   incoming->size = size; | 
 |   incoming->val = val; | 
 |   incoming->next = NULL; | 
 |   UnmapChunk* list = process (incoming, addr, size); | 
 |   return list; | 
 | } | 
 |  | 
 | UnmapChunk* | 
 | HeapMap::munmap (uint64_t addr, int64_t size) | 
 | { | 
 |   UnmapChunk* list = process (NULL, addr, size); | 
 |   return list; | 
 | } | 
 |  | 
 | UnmapChunk* | 
 | HeapMap::process (HeapObj *obj, uint64_t addr, int64_t size) | 
 | { | 
 |   // Some graphics are used to clarify the alignment of mmap regions | 
 |   // obj, shown as consecutive pages: "NNNNNN" | 
 |   // cur, shown as consecutive pages: "CCCCCC" | 
 |  | 
 |   // Find the first overlap, start of N is before end of C.  Examples: | 
 |   //   CCCCC | 
 |   //     NNNNN | 
 |   // or | 
 |   //   CCCCC | 
 |   //    NNN | 
 |   // or | 
 |   //   CCCCC | 
 |   //  NNNNN | 
 |   // or | 
 |   //   CCCCC | 
 |   //  NNNNNNN | 
 |   HeapObj *prev = mmaps; | 
 |   HeapObj *cur = prev->next; | 
 |   while (cur) | 
 |     { | 
 |       if (addr < cur->addr + cur->size) | 
 | 	break; | 
 |       prev = cur; | 
 |       cur = prev->next; | 
 |     } | 
 |  | 
 |   // None found | 
 |   if (cur == NULL) | 
 |     { | 
 |       prev->next = obj; | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   UnmapChunk* list = NULL; | 
 |   if (addr > cur->addr) | 
 |     { | 
 |       if (cur->addr + cur->size <= addr + size) | 
 | 	{ | 
 | 	  // Process overlap on the left (low side) of new allocation | 
 | 	  // New range: N-start to C-end (gets freed below) | 
 | 	  prev = cur; | 
 | 	  cur = getHeapObj (); | 
 | 	  cur->addr = addr; | 
 | 	  cur->size = prev->addr + prev->size - addr; | 
 | 	  cur->val = prev->val; | 
 | 	  cur->next = prev->next; | 
 |  | 
 | 	  // Truncate range: C-start to N-start | 
 | 	  prev->size = addr - prev->addr; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  // Process new allocation that fits completely within old allocation | 
 | 	  // New range: N-start to N-end (freed below) | 
 | 	  int64_t c_end = cur->addr + cur->size; | 
 | 	  prev = cur; | 
 | 	  cur = getHeapObj (); | 
 | 	  cur->addr = addr; | 
 | 	  cur->size = size; | 
 | 	  cur->val = prev->val; | 
 | 	  cur->next = prev->next; | 
 |  | 
 | 	  // Truncate range: C-start to N-start | 
 | 	  prev->size = addr - prev->addr; | 
 |  | 
 | 	  // New range: N-end to C-end. | 
 | 	  HeapObj *next = getHeapObj (); | 
 | 	  next->addr = addr + size; | 
 | 	  next->size = c_end - next->addr; | 
 | 	  next->val = cur->val; | 
 | 	  next->next = cur->next; | 
 | 	  cur->next = next; | 
 | 	} | 
 |     } | 
 |  | 
 |   // Process all full overlaps. | 
 |   // Copy details of cur to UnmapChunk list, remove cur from mmaps | 
 |   while (cur && cur->addr + cur->size <= addr + size) | 
 |     { | 
 |  | 
 |       UnmapChunk* uc = new UnmapChunk; | 
 |       uc->val = cur->val; | 
 |       uc->size = cur->size; | 
 |       uc->next = list; | 
 |       list = uc; | 
 |  | 
 |       HeapObj *t = cur; | 
 |       cur = cur->next; | 
 |       releaseHeapObj (t); | 
 |     } | 
 |  | 
 |   if (cur && cur->addr < addr + size) | 
 |     { | 
 |       // Process the last overlap (right side of new allocation) | 
 |       // Copy details of cur to UnmapChunk list | 
 |       UnmapChunk* uc = new UnmapChunk; | 
 |       uc->val = cur->val; | 
 |       uc->size = addr + size - cur->addr; | 
 |       uc->next = list; | 
 |       list = uc; | 
 |  | 
 |       // Truncate left side of cur | 
 |       cur->size -= uc->size; | 
 |       cur->addr = addr + size; | 
 |     } | 
 |  | 
 |   // Insert new allocation | 
 |   if (obj) | 
 |     { | 
 |       prev->next = obj; | 
 |       obj->next = cur; | 
 |     } | 
 |   else | 
 |     prev->next = cur; | 
 |   return list; | 
 | } |