| /* 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; | |
| } |