blob: ec24c507d8a40c39e5b5f68b1c70cb428bbeb191 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "memory_manager.h"
#include <sys/mman.h>
#include <unistd.h>
#include <cassert>
#ifndef PAGE_SIZE
#define PAGE_SIZE 0x1000
#endif
using namespace interceptor;
MemoryManager::MemoryManager(int prot, int flags)
: prot_(prot), flags_(flags) {}
MemoryManager::~MemoryManager() {
for (const auto &alloc : allocations_)
munmap(reinterpret_cast<void *>(alloc.start()), alloc.size());
}
void *MemoryManager::Allocate(size_t size, size_t alignment,
uintptr_t range_start, uintptr_t range_end) {
assert(size > 0 && "Can't allocate 0 or negative amount of memory.");
assert(size <= PAGE_SIZE && "Can't allocate more then PAGE_SIZE memory");
assert(PAGE_SIZE % alignment == 0 &&
"Can met alignment requiremenet not "
"satisfied by a page boundary");
for (Allocation &alloc : allocations_) {
if (void *addr = alloc.Alloc(size, alignment, range_start, range_end))
return addr;
if (range_start >= alloc.start() && range_start <= alloc.end())
range_start = alloc.end();
}
void *target = mmap(reinterpret_cast<void *>(range_start), PAGE_SIZE, prot_,
flags_, 0, 0);
if (!target) return nullptr;
allocations_.emplace_back(target, PAGE_SIZE);
return allocations_.back().Alloc(size, alignment, range_start, range_end);
}
void *MemoryManager::Allocation::Alloc(size_t size, size_t alignment,
uintptr_t range_start,
uintptr_t range_end) {
size_t new_offset = CalculateNewOffset(size, alignment);
if (new_offset + size > size_) return nullptr; // Doesn't fit
uintptr_t address = reinterpret_cast<uintptr_t>(start_ + new_offset);
if (address < range_start || address > range_end)
return nullptr; // Out of range
offset_ = new_offset + size;
return start_ + new_offset;
}
size_t MemoryManager::Allocation::CalculateNewOffset(size_t size,
size_t alignment) const {
size_t new_offset = offset_;
if (new_offset % alignment != 0)
new_offset += alignment - (new_offset % alignment);
return new_offset;
}