//===-- CodeMemoryManager.cpp - CodeMemoryManager Class -------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See external/llvm/LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the CodeMemoryManager class. // //===----------------------------------------------------------------------===// #define LOG_TAG "bcc" #include <bcc/bcc_assert.h> #include <cutils/log.h> #include "CodeMemoryManager.h" #include "ExecutionEngine/OldJIT/ContextManager.h" #include "llvm/Support/ErrorHandling.h" #include <sys/mman.h> #include <stddef.h> #include <map> #include <string> #include <utility> namespace bcc { const unsigned int MaxCodeSize = ContextManager::ContextCodeSize; const unsigned int MaxGOTSize = 1 * 1024; const unsigned int MaxGlobalVarSize = ContextManager::ContextDataSize; CodeMemoryManager::CodeMemoryManager() : mpCodeMem(NULL), mpGVMem(NULL), mpGOTBase(NULL) { reset(); std::string ErrMsg; mpCodeMem = ContextManager::get().allocateContext(); if (!mpCodeMem) { LOGE("Unable to allocate mpCodeMem\n"); llvm::report_fatal_error("Failed to allocate memory for emitting " "codes\n" + ErrMsg); } // Set global variable pool mpGVMem = mpCodeMem + MaxCodeSize; return; } CodeMemoryManager::~CodeMemoryManager() { 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() { bccAssert(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) { bccAssert(FunctionEnd > FunctionStart); bccAssert(FunctionStart == (getCodeMemBase() + mCurFuncMemIdx) && "Mismatched function start/end!"); // Advance the pointer intptr_t FunctionCodeSize = FunctionEnd - FunctionStart; bccAssert(FunctionCodeSize <= getFreeCodeMemSize() && "Code size excess the limitation!"); mCurFuncMemIdx += FunctionCodeSize; // Record there's a function in our memory start from @FunctionStart bccAssert(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; } } bccAssert((FunctionStart == NULL) && "Memory is never allocated!"); // free the memory intptr_t SizeNeedMove = (getCodeMemBase() + mCurFuncMemIdx) - FunctionEnd; bccAssert(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