blob: 87b99c17ff153e591ba65cd54df80ac10c3006eb [file] [log] [blame]
/*
* Copyright 2010, 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.
*/
#define LOG_TAG "bcc"
#include <cutils/log.h>
#include "CodeMemoryManager.h"
#include "ContextManager.h"
#include "llvm/Support/ErrorHandling.h"
#include <sys/mman.h>
#include <assert.h>
#include <stddef.h>
#include <map>
#include <string>
#include <utility>
namespace bcc {
const unsigned int MaxCodeSize = BCC_CONTEXT_CODE_SIZE;
const unsigned int MaxGOTSize = 1 * 1024;
const unsigned int MaxGlobalVarSize = BCC_CONTEXT_DATA_SIZE;
CodeMemoryManager::CodeMemoryManager()
: mpCodeMem(NULL), mpGVMem(NULL), mpGOTBase(NULL) {
reset();
std::string ErrMsg;
mpCodeMem = allocateContext();
if (!mpCodeMem) {
LOGE("Unable to allocate mpCodeMem\n");
llvm::report_fatal_error("Failed to allocate memory for emitting "
"codes\n" + ErrMsg);
}
LOGI("Allocate mpCodeMem at %p successfully.\n", mpCodeMem);
// Set global variable pool
mpGVMem = mpCodeMem + MaxCodeSize;
return;
}
CodeMemoryManager::~CodeMemoryManager() {
if (mpCodeMem) {
deallocateContext(mpCodeMem);
}
mpCodeMem = 0;
mpGVMem = 0;
}
uint8_t *CodeMemoryManager::allocateSGMemory(uintptr_t Size,
unsigned Alignment) {
intptr_t FreeMemSize = getFreeCodeMemSize();
if ((FreeMemSize < 0) || (static_cast<uintptr_t>(FreeMemSize) < Size))
// The code size excesses our limit
return NULL;
if (Alignment == 0)
Alignment = 1;
uint8_t *result = getCodeMemBase() + mCurSGMemIdx - Size;
result = (uint8_t*) (((intptr_t) result) & ~(intptr_t) (Alignment - 1));
mCurSGMemIdx = result - getCodeMemBase();
return result;
}
// setMemoryWritable - When code generation is in progress, the code pages
// may need permissions changed.
void CodeMemoryManager::setMemoryWritable() {
mprotect(mpCodeMem, MaxCodeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
}
// When code generation is done and we're ready to start execution, the
// code pages may need permissions changed.
void CodeMemoryManager::setMemoryExecutable() {
mprotect(mpCodeMem, MaxCodeSize, PROT_READ | PROT_EXEC);
}
// Setting this flag to true makes the memory manager garbage values over
// freed memory. This is useful for testing and debugging, and is to be
// turned on by default in debug mode.
void CodeMemoryManager::setPoisonMemory(bool poison) {
// no effect
}
// Global Offset Table Management
// If the current table requires a Global Offset Table, this method is
// invoked to allocate it. This method is required to set HasGOT to true.
void CodeMemoryManager::AllocateGOT() {
assert(mpGOTBase != NULL && "Cannot allocate the GOT multiple times");
mpGOTBase = allocateSGMemory(MaxGOTSize);
HasGOT = true;
}
// Main Allocation Functions
// When we start JITing a function, the JIT calls this method to allocate a
// block of free RWX memory, which returns a pointer to it. If the JIT wants
// to request a block of memory of at least a certain size, it passes that
// value as ActualSize, and this method returns a block with at least that
// much space. If the JIT doesn't know ahead of time how much space it will
// need to emit the function, it passes 0 for the ActualSize. In either
// case, this method is required to pass back the size of the allocated
// block through ActualSize. The JIT will be careful to not write more than
// the returned ActualSize bytes of memory.
uint8_t *CodeMemoryManager::startFunctionBody(const llvm::Function *F,
uintptr_t &ActualSize) {
intptr_t FreeMemSize = getFreeCodeMemSize();
if ((FreeMemSize < 0) ||
(static_cast<uintptr_t>(FreeMemSize) < ActualSize))
// The code size excesses our limit
return NULL;
ActualSize = getFreeCodeMemSize();
return (getCodeMemBase() + mCurFuncMemIdx);
}
// This method is called when the JIT is done codegen'ing the specified
// function. At this point we know the size of the JIT compiled function.
// This passes in FunctionStart (which was returned by the startFunctionBody
// method) and FunctionEnd which is a pointer to the actual end of the
// function. This method should mark the space allocated and remember where
// it is in case the client wants to deallocate it.
void CodeMemoryManager::endFunctionBody(const llvm::Function *F,
uint8_t *FunctionStart,
uint8_t *FunctionEnd) {
assert(FunctionEnd > FunctionStart);
assert(FunctionStart == (getCodeMemBase() + mCurFuncMemIdx) &&
"Mismatched function start/end!");
// Advance the pointer
intptr_t FunctionCodeSize = FunctionEnd - FunctionStart;
assert(FunctionCodeSize <= getFreeCodeMemSize() &&
"Code size excess the limitation!");
mCurFuncMemIdx += FunctionCodeSize;
// Record there's a function in our memory start from @FunctionStart
assert(mFunctionMap.find(F) == mFunctionMap.end() &&
"Function already emitted!");
mFunctionMap.insert(
std::make_pair<const llvm::Function*, std::pair<void*, void*> >(
F, std::make_pair(FunctionStart, FunctionEnd)));
return;
}
// Allocate a (function code) memory block of the given size. This method
// cannot be called between calls to startFunctionBody and endFunctionBody.
uint8_t *CodeMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) {
if (getFreeCodeMemSize() < Size) {
// The code size excesses our limit
return NULL;
}
if (Alignment == 0)
Alignment = 1;
uint8_t *result = getCodeMemBase() + mCurFuncMemIdx;
result = (uint8_t*) (((intptr_t) result + Alignment - 1) &
~(intptr_t) (Alignment - 1));
mCurFuncMemIdx = (result + Size) - getCodeMemBase();
return result;
}
// Allocate memory for a global variable.
uint8_t *CodeMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) {
if (getFreeGVMemSize() < Size) {
// The code size excesses our limit
LOGE("No Global Memory");
return NULL;
}
if (Alignment == 0)
Alignment = 1;
uint8_t *result = getGVMemBase() + mCurGVMemIdx;
result = (uint8_t*) (((intptr_t) result + Alignment - 1) &
~(intptr_t) (Alignment - 1));
mCurGVMemIdx = (result + Size) - getGVMemBase();
return result;
}
// Free the specified function body. The argument must be the return value
// from a call to startFunctionBody() that hasn't been deallocated yet. This
// is never called when the JIT is currently emitting a function.
void CodeMemoryManager::deallocateFunctionBody(void *Body) {
// linear search
uint8_t *FunctionStart = NULL, *FunctionEnd = NULL;
for (FunctionMapTy::iterator I = mFunctionMap.begin(),
E = mFunctionMap.end(); I != E; I++) {
if (I->second.first == Body) {
FunctionStart = reinterpret_cast<uint8_t*>(I->second.first);
FunctionEnd = reinterpret_cast<uint8_t*>(I->second.second);
break;
}
}
assert((FunctionStart == NULL) && "Memory is never allocated!");
// free the memory
intptr_t SizeNeedMove = (getCodeMemBase() + mCurFuncMemIdx) - FunctionEnd;
assert(SizeNeedMove >= 0 &&
"Internal error: CodeMemoryManager::mCurFuncMemIdx may not"
" be correctly calculated!");
if (SizeNeedMove > 0) {
// there's data behind deallocating function
memmove(FunctionStart, FunctionEnd, SizeNeedMove);
}
mCurFuncMemIdx -= (FunctionEnd - FunctionStart);
}
// Below are the methods we create
void CodeMemoryManager::reset() {
mpGOTBase = NULL;
HasGOT = false;
mCurFuncMemIdx = 0;
mCurSGMemIdx = MaxCodeSize - 1;
mCurGVMemIdx = 0;
mFunctionMap.clear();
}
} // namespace bcc