/* Copyright (C) 2021-2024 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; }