| /* |
| * 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 "Script.h" |
| |
| #include "CacheReader.h" |
| #include "CacheWriter.h" |
| #include "FileHandle.h" |
| #include "ScriptCompiled.h" |
| #include "ScriptCached.h" |
| #include "Sha1Helper.h" |
| |
| #include <errno.h> |
| |
| #include <new> |
| #include <string.h> |
| #include <cutils/properties.h> |
| |
| |
| namespace { |
| |
| // Input: cacheDir |
| // Input: resName |
| // Input: extName |
| // |
| // Note: cacheFile = resName + extName |
| // |
| // Output: Returns cachePath == cacheDir + cacheFile |
| char *genCacheFileName(const char *cacheDir, |
| const char *resName, |
| const char *extName) { |
| char cachePath[512]; |
| char cacheFile[sizeof(cachePath)]; |
| const size_t kBufLen = sizeof(cachePath) - 1; |
| |
| cacheFile[0] = '\0'; |
| // Note: resName today is usually something like |
| // "/com.android.fountain:raw/fountain" |
| if (resName[0] != '/') { |
| // Get the absolute path of the raw/***.bc file. |
| |
| // Generate the absolute path. This doesn't do everything it |
| // should, e.g. if resName is "./out/whatever" it doesn't crunch |
| // the leading "./" out because this if-block is not triggered, |
| // but it'll make do. |
| // |
| if (getcwd(cacheFile, kBufLen) == NULL) { |
| LOGE("Can't get CWD while opening raw/***.bc file\n"); |
| return NULL; |
| } |
| // Append "/" at the end of cacheFile so far. |
| strncat(cacheFile, "/", kBufLen); |
| } |
| |
| // cacheFile = resName + extName |
| // |
| strncat(cacheFile, resName, kBufLen); |
| if (extName != NULL) { |
| // TODO(srhines): strncat() is a bit dangerous |
| strncat(cacheFile, extName, kBufLen); |
| } |
| |
| // Turn the path into a flat filename by replacing |
| // any slashes after the first one with '@' characters. |
| char *cp = cacheFile + 1; |
| while (*cp != '\0') { |
| if (*cp == '/') { |
| *cp = '@'; |
| } |
| cp++; |
| } |
| |
| // Tack on the file name for the actual cache file path. |
| strncpy(cachePath, cacheDir, kBufLen); |
| strncat(cachePath, cacheFile, kBufLen); |
| |
| LOGV("Cache file for '%s' '%s' is '%s'\n", resName, extName, cachePath); |
| return strdup(cachePath); |
| } |
| |
| 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() { |
| if (mStatus == ScriptStatus::Compiled) { |
| delete mCompiled; |
| } else if (mStatus == ScriptStatus::Cached) { |
| delete mCached; |
| } |
| } |
| |
| |
| int Script::readBC(const char *bitcode, |
| size_t bitcodeSize, |
| const BCCchar *resName, |
| const BCCchar *cacheDir) { |
| if (mStatus != ScriptStatus::Unknown) { |
| mErrorCode = BCC_INVALID_OPERATION; |
| LOGE("Invalid operation: %s\n", __func__); |
| return 1; |
| } |
| |
| sourceBC = bitcode; |
| sourceResName = resName; |
| sourceSize = bitcodeSize; |
| |
| if (cacheDir && resName) { |
| cacheFile = genCacheFileName(cacheDir, resName, ".oBCC"); |
| } |
| |
| return 0; |
| } |
| |
| |
| int Script::readModule(llvm::Module *module) { |
| if (mStatus != ScriptStatus::Unknown) { |
| mErrorCode = BCC_INVALID_OPERATION; |
| LOGE("Invalid operation: %s\n", __func__); |
| return 1; |
| } |
| |
| sourceModule = module; |
| return 0; |
| } |
| |
| |
| int Script::linkBC(const char *bitcode, size_t bitcodeSize) { |
| if (mStatus != ScriptStatus::Unknown) { |
| mErrorCode = BCC_INVALID_OPERATION; |
| LOGE("Invalid operation: %s\n", __func__); |
| return 1; |
| } |
| |
| libraryBC = bitcode; |
| librarySize = bitcodeSize; |
| LOGI("libraryBC = %x, librarySize = %d", libraryBC, librarySize); |
| return 0; |
| } |
| |
| |
| int Script::compile() { |
| if (mStatus != ScriptStatus::Unknown) { |
| mErrorCode = BCC_INVALID_OPERATION; |
| LOGE("Invalid operation: %s\n", __func__); |
| return 1; |
| } |
| |
| // Load Cache File |
| if (cacheFile && internalLoadCache() == 0) { |
| return 0; |
| } |
| |
| return internalCompile(); |
| } |
| |
| |
| int Script::internalLoadCache() { |
| if (getBooleanProp("debug.bcc.nocache")) { |
| // Android system environment property disable the cache mechanism by |
| // setting "debug.bcc.nocache". So we will not load the cache file any |
| // way. |
| return 1; |
| } |
| |
| if (!cacheFile) { |
| // The application developer has not specify resName or cacheDir, so |
| // we don't know where to open the cache file. |
| return 1; |
| } |
| |
| if (sourceBC) { |
| // If we are going to create cache file. We have to calculate sha1sum |
| // first (no matter we can open the file now or not.) |
| calcSHA1(sourceSHA1, sourceBC, sourceSize); |
| } |
| |
| FileHandle file; |
| |
| if (file.open(cacheFile, OpenMode::Read) < 0) { |
| // Unable to open the cache file in read mode. |
| return 1; |
| } |
| |
| CacheReader reader; |
| |
| // Dependencies |
| #if defined(USE_LIBBCC_SHA1SUM) |
| reader.addDependency(BCC_FILE_RESOURCE, pathLibBCC, sha1LibBCC); |
| #endif |
| |
| reader.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS); |
| |
| if (sourceBC) { |
| reader.addDependency(BCC_APK_RESOURCE, sourceResName, sourceSHA1); |
| } |
| |
| // Read cache file |
| ScriptCached *cached = reader.readCacheFile(&file, this); |
| if (!cached) { |
| 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() { |
| // Create the ScriptCompiled object |
| mCompiled = new (nothrow) ScriptCompiled(this); |
| |
| if (!mCompiled) { |
| mErrorCode = BCC_OUT_OF_MEMORY; |
| LOGE("Out of memory: %s %d\n", __FILE__, __LINE__); |
| return 1; |
| } |
| |
| mStatus = ScriptStatus::Compiled; |
| |
| // Register symbol lookup function |
| if (mpExtSymbolLookupFn) { |
| mCompiled->registerSymbolCallback(mpExtSymbolLookupFn, |
| mpExtSymbolLookupFnContext); |
| } |
| |
| // Setup the source bitcode / module |
| if (sourceBC) { |
| if (mCompiled->readBC(sourceBC, sourceSize, sourceResName, 0) != 0) { |
| LOGE("Unable to readBC, bitcode=%p, size=%lu\n", |
| sourceBC, (unsigned long)sourceSize); |
| return 1; |
| } |
| |
| LOGI("Load sourceBC\n"); |
| } else if (sourceModule) { |
| if (mCompiled->readModule(sourceModule) != 0) { |
| return 1; |
| } |
| |
| LOGI("Load sourceModule\n"); |
| } |
| |
| // Link the source module with the library module |
| if (libraryBC) { |
| if (mCompiled->linkBC(libraryBC, librarySize) != 0) { |
| return 1; |
| } |
| |
| LOGE("Load Library\n"); |
| } |
| |
| // Compile and JIT the code |
| if (mCompiled->compile() != 0) { |
| LOGE("Unable to compile.\n"); |
| return 1; |
| } |
| |
| // TODO(logan): Write the cache out |
| if (cacheFile && !getBooleanProp("debug.bcc.nocache")) { |
| FileHandle file; |
| |
| if (file.open(cacheFile, OpenMode::Write) >= 0) { |
| CacheWriter writer; |
| |
| // Dependencies |
| #if defined(USE_LIBBCC_SHA1SUM) |
| writer.addDependency(BCC_FILE_RESOURCE, pathLibBCC, sha1LibBCC); |
| #endif |
| writer.addDependency(BCC_FILE_RESOURCE, pathLibRS, sha1LibRS); |
| |
| if (sourceBC) { |
| writer.addDependency(BCC_APK_RESOURCE, sourceResName, sourceSHA1); |
| } |
| |
| // 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(&file, this, libRS_threadable)) { |
| file.truncate(); |
| file.close(); |
| |
| if (unlink(cacheFile) != 0) { |
| LOGE("Unable to remove the invalid cache file: %s. (reason: %s)\n", |
| cacheFile, 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; |
| } |
| } |
| |
| |
| void Script::getExportVars(BCCsizei *actualVarCount, |
| BCCsizei maxVarCount, |
| BCCvoid **vars) { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| mCompiled->getExportVars(actualVarCount, maxVarCount, vars); |
| break; |
| |
| case ScriptStatus::Cached: |
| mCached->getExportVars(actualVarCount, maxVarCount, vars); |
| break; |
| |
| default: |
| mErrorCode = BCC_INVALID_OPERATION; |
| } |
| } |
| |
| |
| void Script::getExportFuncs(BCCsizei *actualFuncCount, |
| BCCsizei maxFuncCount, |
| BCCvoid **funcs) { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| mCompiled->getExportFuncs(actualFuncCount, maxFuncCount, funcs); |
| break; |
| |
| case ScriptStatus::Cached: |
| mCached->getExportFuncs(actualFuncCount, maxFuncCount, funcs); |
| break; |
| |
| default: |
| mErrorCode = BCC_INVALID_OPERATION; |
| } |
| } |
| |
| |
| void Script::getPragmas(BCCsizei *actualStringCount, |
| BCCsizei maxStringCount, |
| BCCchar **strings) { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| mCompiled->getPragmas(actualStringCount, maxStringCount, strings); |
| break; |
| |
| case ScriptStatus::Cached: |
| mCached->getPragmas(actualStringCount, maxStringCount, strings); |
| break; |
| |
| default: |
| mErrorCode = BCC_INVALID_OPERATION; |
| } |
| } |
| |
| |
| void Script::getFunctions(BCCsizei *actualFunctionCount, |
| BCCsizei maxFunctionCount, |
| BCCchar **functions) { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| mCompiled->getFunctions(actualFunctionCount, maxFunctionCount, functions); |
| break; |
| |
| case ScriptStatus::Cached: |
| mCached->getFunctions(actualFunctionCount, maxFunctionCount, functions); |
| break; |
| |
| default: |
| mErrorCode = BCC_INVALID_OPERATION; |
| } |
| } |
| |
| char *Script::getContext() { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| return mCompiled->getContext(); |
| |
| case ScriptStatus::Cached: |
| return mCached->getContext(); |
| |
| default: |
| mErrorCode = BCC_INVALID_OPERATION; |
| return NULL; |
| } |
| } |
| |
| |
| void Script::getFunctionBinary(BCCchar *function, |
| BCCvoid **base, |
| BCCsizei *length) { |
| switch (mStatus) { |
| case ScriptStatus::Compiled: |
| mCompiled->getFunctionBinary(function, base, length); |
| return; |
| |
| case ScriptStatus::Cached: |
| mCached->getFunctionBinary(function, base, length); |
| return; |
| |
| default: |
| *base = NULL; |
| *length = 0; |
| return; |
| } |
| |
| } |
| |
| |
| void Script::registerSymbolCallback(BCCSymbolLookupFn pFn, BCCvoid *pContext) { |
| mpExtSymbolLookupFn = pFn; |
| mpExtSymbolLookupFnContext = pContext; |
| |
| if (mStatus != ScriptStatus::Unknown) { |
| mErrorCode = BCC_INVALID_OPERATION; |
| LOGE("Invalid operation: %s\n", __func__); |
| } |
| } |
| |
| } // namespace bcc |