/* * copyright 2010-2012, 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 "Script.h" #include "Config.h" #include "bcinfo/BitcodeWrapper.h" #include "MCCacheReader.h" #include "MCCacheWriter.h" #include "CompilerOption.h" #include "DebugHelper.h" #include "FileHandle.h" #include "GDBJITRegistrar.h" #include "ScriptCompiled.h" #include "ScriptCached.h" #include "Sha1Helper.h" #include "SourceInfo.h" #include #include #include #include #include #include #include namespace { bool getBooleanProp(const char *str) { char buf[PROPERTY_VALUE_MAX]; property_get(str, buf, "0"); return strcmp(buf, "0") != 0; } } // namespace anonymous namespace bcc { Script::~Script() { switch (mStatus) { case ScriptStatus::Compiled: delete mCompiled; break; case ScriptStatus::Cached: delete mCached; break; default: break; } for (size_t i = 0; i < 2; ++i) { delete mSourceList[i]; } } int Script::addSourceBC(size_t idx, char const *resName, const char *bitcode, size_t bitcodeSize, unsigned long flags) { if (!resName) { mErrorCode = BCC_INVALID_VALUE; ALOGE("Invalid argument: resName = NULL\n"); return 1; } if (mStatus != ScriptStatus::Unknown) { mErrorCode = BCC_INVALID_OPERATION; ALOGE("Bad operation: Adding source after bccPrepareExecutable\n"); return 1; } if (!bitcode) { mErrorCode = BCC_INVALID_VALUE; ALOGE("Invalid argument: bitcode = NULL\n"); return 1; } bcinfo::BitcodeWrapper wrapper(bitcode, bitcodeSize); mSourceList[idx] = SourceInfo::createFromBuffer(resName, bitcode, bitcodeSize, flags); if (!mSourceList[idx]) { mErrorCode = BCC_OUT_OF_MEMORY; ALOGE("Out of memory while adding source bitcode\n"); return 1; } return 0; } int Script::addSourceModule(size_t idx, llvm::Module *module, unsigned long flags) { if (mStatus != ScriptStatus::Unknown) { mErrorCode = BCC_INVALID_OPERATION; ALOGE("Bad operation: Adding source after bccPrepareExecutable\n"); return 1; } if (!module) { mErrorCode = BCC_INVALID_VALUE; ALOGE("Invalid argument: module = NULL\n"); return 1; } mSourceList[idx] = SourceInfo::createFromModule(module, flags); if (!mSourceList[idx]) { mErrorCode = BCC_OUT_OF_MEMORY; ALOGE("Out of memory when add source module\n"); return 1; } return 0; } int Script::addSourceFile(size_t idx, char const *path, unsigned long flags) { if (mStatus != ScriptStatus::Unknown) { mErrorCode = BCC_INVALID_OPERATION; ALOGE("Bad operation: Adding source after bccPrepareExecutable\n"); return 1; } if (!path) { mErrorCode = BCC_INVALID_VALUE; ALOGE("Invalid argument: path = NULL\n"); return 1; } struct stat sb; if (stat(path, &sb) != 0) { mErrorCode = BCC_INVALID_VALUE; ALOGE("File not found: %s\n", path); return 1; } mSourceList[idx] = SourceInfo::createFromFile(path, flags); if (!mSourceList[idx]) { mErrorCode = BCC_OUT_OF_MEMORY; ALOGE("Out of memory while adding source file\n"); return 1; } return 0; } int Script::prepareRelocatable(char const *objPath, llvm::Reloc::Model RelocModel, unsigned long flags) { CompilerOption option; option.RelocModelOpt = RelocModel; option.LoadAfterCompile = false; int status = internalCompile(option); if (status != 0) { ALOGE("LLVM error message: %s\n", getCompilerErrorMessage()); return status; } FileHandle objFile; if (objFile.open(objPath, OpenMode::Write) < 0) { ALOGE("Failed to open %s for write.\n", objPath); return 1; } if (static_cast(objFile.write(getELF(), getELFSize())) != getELFSize()) { objFile.close(); ::unlink(objPath); ALOGE("Unable to write ELF to file %s.\n", objPath); return false; } mObjectType = ScriptObject::Relocatable; return 0; } int Script::prepareSharedObject(char const *objPath, char const *dsoPath, unsigned long flags) { // TODO: Support cached shared object. return 1; } int Script::prepareExecutable(char const *cacheDir, char const *cacheName, unsigned long flags) { if (mStatus != ScriptStatus::Unknown) { mErrorCode = BCC_INVALID_OPERATION; ALOGE("Invalid operation: %s\n", __func__); return 1; } int status = internalLoadCache(cacheDir, cacheName, /* checkOnly */ false); if (status != 0) { CompilerOption option; status = internalCompile(option); if (status != 0) { ALOGE("LLVM error message: %s\n", getCompilerErrorMessage()); return status; } status = writeCache(); if (status != 0) { ALOGE("Failed to write the cache for %s\n", cacheName); return status; } } // FIXME: Registration can be conditional on the presence of debug metadata registerObjectWithGDB(getELF(), getELFSize()); // thread-safe registration mObjectType = ScriptObject::Executable; return status; } int Script::internalLoadCache(char const *cacheDir, char const *cacheName, bool checkOnly) { if ((cacheDir == NULL) || (cacheName == NULL)) { return 1; } // Set cache file Name mCacheName = cacheName; // Santize mCacheDir. Ensure that mCacheDir ends with '/'. mCacheDir = cacheDir; if (!mCacheDir.empty() && *mCacheDir.rbegin() != '/') { mCacheDir.push_back('/'); } if (!isCacheable()) { return 1; } std::string objPath = getCachedObjectPath(); std::string infoPath = getCacheInfoPath(); FileHandle objFile; if (objFile.open(objPath.c_str(), OpenMode::Read) < 0) { // Unable to open the executable file in read mode. return 1; } FileHandle infoFile; if (infoFile.open(infoPath.c_str(), OpenMode::Read) < 0) { // Unable to open the metadata information file in read mode. return 1; } MCCacheReader reader; // Register symbol lookup function if (mpExtSymbolLookupFn) { reader.registerSymbolCallback(mpExtSymbolLookupFn, mpExtSymbolLookupFnContext); } // Dependencies reader.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1); reader.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS); for (size_t i = 0; i < 2; ++i) { if (mSourceList[i]) { mSourceList[i]->introDependency(reader); } } if (checkOnly) return !reader.checkCacheFile(&objFile, &infoFile, this); // Read cache file ScriptCached *cached = reader.readCacheFile(&objFile, &infoFile, this); if (!cached) { mIsContextSlotNotAvail = reader.isContextSlotNotAvail(); return 1; } mCached = cached; mStatus = ScriptStatus::Cached; // Dirty hack for libRS. // TODO(all): This dirty hack should be removed in the future. if (!cached->isLibRSThreadable() && mpExtSymbolLookupFn) { mpExtSymbolLookupFn(mpExtSymbolLookupFnContext, "__clearThreadable"); } return 0; } int Script::internalCompile(const CompilerOption &option) { // Create the ScriptCompiled object mCompiled = new (std::nothrow) ScriptCompiled(this); if (!mCompiled) { mErrorCode = BCC_OUT_OF_MEMORY; ALOGE("Out of memory: %s %d\n", __FILE__, __LINE__); return 1; } mStatus = ScriptStatus::Compiled; // Register symbol lookup function if (mpExtSymbolLookupFn) { mCompiled->registerSymbolCallback(mpExtSymbolLookupFn, mpExtSymbolLookupFnContext); } if (!mSourceList[0]) { ALOGE("Source bitcode is not set.\n"); return 1; } // Parse Source bitcode file (if necessary) if (mSourceList[0]->prepareModule() != 0) { ALOGE("Unable to setup source module\n"); return 1; } // Parse Library bitcode file (if necessary) if (mSourceList[1]) { if (mSourceList[1]->prepareModule(mSourceList[0]->getContext()) != 0) { ALOGE("Unable to setup library module\n"); return 1; } } // Set the main source module if (mCompiled->readModule(mSourceList[0]->getModule()) != 0) { ALOGE("Unable to read source module\n"); return 1; } // Link the source module with the library module if (mSourceList[1]) { if (mCompiled->linkModule(mSourceList[1]->getModule()) != 0) { ALOGE("Unable to link library module\n"); return 1; } } // Compile and JIT the code if (mCompiled->compile(option) != 0) { ALOGE("Unable to compile.\n"); return 1; } return 0; } int Script::writeCache() { // Not compiled script or encountered error during the compilation. if ((mStatus != ScriptStatus::Compiled) || (getCompilerErrorMessage() == NULL)) return 1; // Note: If we re-compile the script because the cached context slot not // available, then we don't have to write the cache. // Note: If the address of the context is not in the context slot, then // we don't have to cache it. if (isCacheable()) { std::string objPath = getCachedObjectPath(); std::string infoPath = getCacheInfoPath(); // Remove the file if it already exists before writing the new file. // The old file may still be mapped elsewhere in memory and we do not want // to modify its contents. (The same script may be running concurrently in // the same process or a different process!) ::unlink(objPath.c_str()); ::unlink(infoPath.c_str()); FileHandle objFile; FileHandle infoFile; if (objFile.open(objPath.c_str(), OpenMode::Write) >= 0 && infoFile.open(infoPath.c_str(), OpenMode::Write) >= 0) { MCCacheWriter writer; #ifdef TARGET_BUILD // Dependencies writer.addDependency(BCC_FILE_RESOURCE, pathLibBCC_SHA1, sha1LibBCC_SHA1); writer.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS); #endif for (size_t i = 0; i < 2; ++i) { if (mSourceList[i]) { mSourceList[i]->introDependency(writer); } } // libRS is threadable dirty hack // TODO: This should be removed in the future uint32_t libRS_threadable = 0; if (mpExtSymbolLookupFn) { libRS_threadable = (uint32_t)mpExtSymbolLookupFn(mpExtSymbolLookupFnContext, "__isThreadable"); } if (!writer.writeCacheFile(&objFile, &infoFile, this, libRS_threadable)) { objFile.truncate(); objFile.close(); if (unlink(objPath.c_str()) != 0) { ALOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n", objPath.c_str(), strerror(errno)); } infoFile.truncate(); infoFile.close(); if (unlink(infoPath.c_str()) != 0) { ALOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n", infoPath.c_str(), strerror(errno)); } } } } return 0; } char const *Script::getCompilerErrorMessage() { if (mStatus != ScriptStatus::Compiled) { mErrorCode = BCC_INVALID_OPERATION; return NULL; } return mCompiled->getCompilerErrorMessage(); } void *Script::lookup(const char *name) { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->lookup(name); } case ScriptStatus::Cached: { return mCached->lookup(name); } default: { mErrorCode = BCC_INVALID_OPERATION; return NULL; } } } size_t Script::getExportVarCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportVarCount(); } case ScriptStatus::Cached: { return mCached->getExportVarCount(); } default: { return 0; } } } size_t Script::getExportFuncCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportFuncCount(); } case ScriptStatus::Cached: { return mCached->getExportFuncCount(); } default: { return 0; } } } size_t Script::getExportForEachCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportForEachCount(); } case ScriptStatus::Cached: { return mCached->getExportForEachCount(); } default: { return 0; } } } size_t Script::getPragmaCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getPragmaCount(); } case ScriptStatus::Cached: { return mCached->getPragmaCount(); } default: { return 0; } } } size_t Script::getFuncCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getFuncCount(); } case ScriptStatus::Cached: { return mCached->getFuncCount(); } default: { return 0; } } } size_t Script::getObjectSlotCount() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getObjectSlotCount(); } case ScriptStatus::Cached: { return mCached->getObjectSlotCount(); } default: { return 0; } } } void Script::getExportVarList(size_t varListSize, void **varList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getExportVarList(varListSize, varList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getExportVarNameList(std::vector &varList) { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportVarNameList(varList); } default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getExportFuncList(size_t funcListSize, void **funcList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getExportFuncList(funcListSize, funcList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getExportFuncNameList(std::vector &funcList) { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportFuncNameList(funcList); } default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getExportForEachList(size_t funcListSize, void **funcList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getExportForEachList(funcListSize, funcList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getExportForEachNameList(std::vector &forEachList) { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getExportForEachNameList(forEachList); } default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getPragmaList(size_t pragmaListSize, char const **keyList, char const **valueList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getPragmaList(pragmaListSize, keyList, valueList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getFuncInfoList(size_t funcInfoListSize, FuncInfo *funcInfoList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getFuncInfoList(funcInfoListSize, funcInfoList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } void Script::getObjectSlotList(size_t objectSlotListSize, uint32_t *objectSlotList) { switch (mStatus) { #define DELEGATE(STATUS) \ case ScriptStatus::STATUS: \ m##STATUS->getObjectSlotList(objectSlotListSize, objectSlotList); \ break; DELEGATE(Cached); DELEGATE(Compiled); #undef DELEGATE default: { mErrorCode = BCC_INVALID_OPERATION; } } } int Script::registerSymbolCallback(BCCSymbolLookupFn pFn, void *pContext) { mpExtSymbolLookupFn = pFn; mpExtSymbolLookupFnContext = pContext; if (mStatus != ScriptStatus::Unknown) { mErrorCode = BCC_INVALID_OPERATION; ALOGE("Invalid operation: %s\n", __func__); return 1; } return 0; } bool Script::isCacheable() const { if (getBooleanProp("debug.bcc.nocache")) { // Android system environment property: Disables the cache mechanism by // setting "debug.bcc.nocache". So we will not load the cache file any // way. return false; } if (mCacheDir.empty() || mCacheName.empty()) { // The application developer has not specified the cachePath, so // we don't know where to open the cache file. return false; } return true; } size_t Script::getELFSize() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getELFSize(); } case ScriptStatus::Cached: { return mCached->getELFSize(); } default: { return 0; } } } const char *Script::getELF() const { switch (mStatus) { case ScriptStatus::Compiled: { return mCompiled->getELF(); } case ScriptStatus::Cached: { return mCached->getELF(); } default: { return NULL; } } } } // namespace bcc