Home | History | Annotate | Download | only in cpu_ref
      1 #include "rsCpuExecutable.h"
      2 #include "rsCppUtils.h"
      3 
      4 #include <fstream>
      5 #include <set>
      6 #include <memory>
      7 
      8 #include <sys/stat.h>
      9 
     10 #ifdef RS_COMPATIBILITY_LIB
     11 #include <stdio.h>
     12 #else
     13 #include "bcc/Config.h"
     14 #endif
     15 
     16 #include <unistd.h>
     17 #include <dlfcn.h>
     18 #include <sys/stat.h>
     19 
     20 namespace android {
     21 namespace renderscript {
     22 
     23 namespace {
     24 
     25 // Check if a path exists and attempt to create it if it doesn't.
     26 static bool ensureCacheDirExists(const char *path) {
     27     if (access(path, R_OK | W_OK | X_OK) == 0) {
     28         // Done if we can rwx the directory
     29         return true;
     30     }
     31     if (mkdir(path, 0700) == 0) {
     32         return true;
     33     }
     34     return false;
     35 }
     36 
     37 // Copy the file named \p srcFile to \p dstFile.
     38 // Return 0 on success and -1 if anything wasn't copied.
     39 static int copyFile(const char *dstFile, const char *srcFile) {
     40     std::ifstream srcStream(srcFile);
     41     if (!srcStream) {
     42         ALOGE("Could not verify or read source file: %s", srcFile);
     43         return -1;
     44     }
     45     std::ofstream dstStream(dstFile);
     46     if (!dstStream) {
     47         ALOGE("Could not verify or write destination file: %s", dstFile);
     48         return -1;
     49     }
     50     dstStream << srcStream.rdbuf();
     51     if (!dstStream) {
     52         ALOGE("Could not write destination file: %s", dstFile);
     53         return -1;
     54     }
     55 
     56     srcStream.close();
     57     dstStream.close();
     58 
     59     return 0;
     60 }
     61 
     62 static std::string findSharedObjectName(const char *cacheDir,
     63                                         const char *resName,
     64                                         const bool reuse = true) {
     65     std::string scriptSOName(cacheDir);
     66 #if defined(RS_COMPATIBILITY_LIB) && !defined(__LP64__)
     67     size_t cutPos = scriptSOName.rfind("cache");
     68     if (cutPos != std::string::npos) {
     69         scriptSOName.erase(cutPos);
     70     } else {
     71         ALOGE("Found peculiar cacheDir (missing \"cache\"): %s", cacheDir);
     72     }
     73     scriptSOName.append("/lib/librs.");
     74 #else
     75     scriptSOName.append("/librs.");
     76 #endif // RS_COMPATIBILITY_LIB
     77     scriptSOName.append(resName);
     78     if (!reuse) {
     79         // If the generated shared library is not reused, e.g., with a debug
     80         // context or forced by a system property, multiple threads may read
     81         // and write the shared library at the same time. To avoid the race
     82         // on the generated shared library, delete it before finishing script
     83         // initialization. To avoid deleting a file generated by a regular
     84         // context, use a special suffix here.
     85         // Because the script initialization is guarded by a lock from the Java
     86         // API, it is safe to name this file with a consistent name and suffix
     87         // and delete it after loading. The same lock has also prevented write-
     88         // write races on the .so during script initialization even if reuse is
     89         // true.
     90         scriptSOName.append("#delete_after_load");
     91     }
     92     scriptSOName.append(".so");
     93 
     94     return scriptSOName;
     95 }
     96 
     97 #ifndef RS_COMPATIBILITY_LIB
     98 static bool isRunningInVndkNamespace() {
     99     static bool result = []() {
    100         Dl_info info;
    101         if (dladdr(reinterpret_cast<const void*>(&isRunningInVndkNamespace), &info) != 0) {
    102             std::string filename = std::string(info.dli_fname);
    103             return filename.find("/vndk-sp") != std::string::npos;
    104         } else {
    105             ALOGW("Can't determine whether this lib is running in vndk namespace or not. Assuming it is in vndk namespace.");
    106         }
    107         return true;
    108     }();
    109     return result;
    110 }
    111 #endif
    112 
    113 }  // anonymous namespace
    114 
    115 const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc";
    116 const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache";
    117 
    118 #ifndef RS_COMPATIBILITY_LIB
    119 
    120 bool SharedLibraryUtils::createSharedLibrary(const char *driverName,
    121                                              const char *cacheDir,
    122                                              const char *resName,
    123                                              const bool reuse,
    124                                              std::string *fullPath) {
    125     std::string sharedLibName = findSharedObjectName(cacheDir, resName, reuse);
    126     if (fullPath) {
    127         *fullPath = sharedLibName;
    128     }
    129     std::string objFileName = cacheDir;
    130     objFileName.append("/");
    131     objFileName.append(resName);
    132     objFileName.append(".o");
    133     // Should be something like "libRSDriver.so".
    134     std::string linkDriverName = driverName;
    135     // Remove ".so" and replace "lib" with "-l".
    136     // This will leave us with "-lRSDriver" instead.
    137     linkDriverName.erase(linkDriverName.length() - 3);
    138     linkDriverName.replace(0, 3, "-l");
    139 
    140     static const std::string vndkLibCompilerRt =
    141         getVndkSysLibPath() + "/libcompiler_rt.so";
    142     const char *compiler_rt = isRunningInVndkNamespace() ?
    143         vndkLibCompilerRt.c_str() : SYSLIBPATH "/libcompiler_rt.so";
    144     const char *mTriple = "-mtriple=" DEFAULT_TARGET_TRIPLE_STRING;
    145     const char *libPath = "--library-path=" SYSLIBPATH;
    146     // vndk path is only added when RS framework is running in vndk namespace.
    147     // If we unconditionally add the vndk path to the library path, then RS
    148     // driver in the vndk-sp directory will always be used even for CPU fallback
    149     // case, where RS framework is loaded from the default namespace.
    150     static const std::string vndkLibPathString =
    151         "--library-path=" + getVndkSysLibPath();
    152     const char *vndkLibPath = isRunningInVndkNamespace() ?
    153         vndkLibPathString.c_str() : "";
    154     const char *vendorLibPath = "--library-path=" SYSLIBPATH_VENDOR;
    155 
    156     // The search path order should be vendor -> vndk -> system
    157     std::vector<const char *> args = {
    158         LD_EXE_PATH,
    159         "-shared",
    160         "-nostdlib",
    161         compiler_rt, mTriple, vendorLibPath, vndkLibPath, libPath,
    162         linkDriverName.c_str(), "-lm", "-lc",
    163         objFileName.c_str(),
    164         "-o", sharedLibName.c_str(),
    165         nullptr
    166     };
    167 
    168     return rsuExecuteCommand(LD_EXE_PATH, args.size()-1, args.data());
    169 
    170 }
    171 
    172 #endif  // RS_COMPATIBILITY_LIB
    173 
    174 const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
    175 
    176 void* SharedLibraryUtils::loadAndDeleteSharedLibrary(const char *fullPath) {
    177     void *loaded = dlopen(fullPath, RTLD_NOW | RTLD_LOCAL);
    178     if (loaded == nullptr) {
    179         ALOGE("Unable to open shared library (%s): %s", fullPath, dlerror());
    180         return nullptr;
    181     }
    182 
    183     int r = unlink(fullPath);
    184     if (r != 0) {
    185         ALOGE("Could not unlink copy %s", fullPath);
    186         return nullptr;
    187     }
    188     return loaded;
    189 }
    190 
    191 void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir,
    192                                             const char *resName,
    193                                             const char *nativeLibDir,
    194                                             bool* alreadyLoaded) {
    195     void *loaded = nullptr;
    196 
    197 #if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
    198     std::string scriptSOName = findSharedObjectName(nativeLibDir, resName);
    199 #else
    200     std::string scriptSOName = findSharedObjectName(cacheDir, resName);
    201 #endif
    202 
    203     // We should check if we can load the library from the standard app
    204     // location for shared libraries first.
    205     loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName, alreadyLoaded);
    206 
    207     if (loaded == nullptr) {
    208         ALOGE("Unable to open shared library (%s): %s",
    209               scriptSOName.c_str(), dlerror());
    210 
    211 #ifdef RS_COMPATIBILITY_LIB
    212         // One final attempt to find the library in "/system/lib".
    213         // We do this to allow bundled applications to use the compatibility
    214         // library fallback path. Those applications don't have a private
    215         // library path, so they need to install to the system directly.
    216         // Note that this is really just a testing path.
    217         std::string scriptSONameSystem("/system/lib/librs.");
    218         scriptSONameSystem.append(resName);
    219         scriptSONameSystem.append(".so");
    220         loaded = loadSOHelper(scriptSONameSystem.c_str(), cacheDir,
    221                               resName);
    222         if (loaded == nullptr) {
    223             ALOGE("Unable to open system shared library (%s): %s",
    224                   scriptSONameSystem.c_str(), dlerror());
    225         }
    226 #endif
    227     }
    228 
    229     return loaded;
    230 }
    231 
    232 std::string SharedLibraryUtils::getRandomString(size_t len) {
    233     char buf[len + 1];
    234     for (size_t i = 0; i < len; i++) {
    235         uint32_t r = arc4random() & 0xffff;
    236         r %= 62;
    237         if (r < 26) {
    238             // lowercase
    239             buf[i] = 'a' + r;
    240         } else if (r < 52) {
    241             // uppercase
    242             buf[i] = 'A' + (r - 26);
    243         } else {
    244             // Use a number
    245             buf[i] = '0' + (r - 52);
    246         }
    247     }
    248     buf[len] = '\0';
    249     return std::string(buf);
    250 }
    251 
    252 void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
    253                                        const char *resName, bool *alreadyLoaded) {
    254     // Keep track of which .so libraries have been loaded. Once a library is
    255     // in the set (per-process granularity), we must instead make a copy of
    256     // the original shared object (randomly named .so file) and load that one
    257     // instead. If we don't do this, we end up aliasing global data between
    258     // the various Script instances (which are supposed to be completely
    259     // independent).
    260     static std::set<std::string> LoadedLibraries;
    261 
    262     void *loaded = nullptr;
    263 
    264     // Skip everything if we don't even have the original library available.
    265     if (access(origName, F_OK) != 0) {
    266         return nullptr;
    267     }
    268 
    269     // Common path is that we have not loaded this Script/library before.
    270     if (LoadedLibraries.find(origName) == LoadedLibraries.end()) {
    271         if (alreadyLoaded != nullptr) {
    272             *alreadyLoaded = false;
    273         }
    274         loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
    275         if (loaded) {
    276             LoadedLibraries.insert(origName);
    277         }
    278         return loaded;
    279     }
    280 
    281     if (alreadyLoaded != nullptr) {
    282         *alreadyLoaded = true;
    283     }
    284 
    285     std::string newName(cacheDir);
    286 
    287     // Append RS_CACHE_DIR only if it is not found in cacheDir
    288     // In driver mode, RS_CACHE_DIR is already appended to cacheDir.
    289     if (newName.find(RS_CACHE_DIR) == std::string::npos) {
    290         newName.append("/");
    291         newName.append(RS_CACHE_DIR);
    292         newName.append("/");
    293     }
    294 
    295     if (!ensureCacheDirExists(newName.c_str())) {
    296         ALOGE("Could not verify or create cache dir: %s", cacheDir);
    297         return nullptr;
    298     }
    299 
    300     // Construct an appropriately randomized filename for the copy.
    301     newName.append("librs.");
    302     newName.append(resName);
    303     newName.append("#");
    304     newName.append(getRandomString(6).c_str());  // 62^6 potential filename variants.
    305     newName.append(".so");
    306 
    307     int r = copyFile(newName.c_str(), origName);
    308     if (r != 0) {
    309         ALOGE("Could not create copy %s -> %s", origName, newName.c_str());
    310         return nullptr;
    311     }
    312     loaded = dlopen(newName.c_str(), RTLD_NOW | RTLD_LOCAL);
    313     r = unlink(newName.c_str());
    314     if (r != 0) {
    315         ALOGE("Could not unlink copy %s", newName.c_str());
    316     }
    317     if (loaded) {
    318         LoadedLibraries.insert(newName.c_str());
    319     }
    320 
    321     return loaded;
    322 }
    323 
    324 // MAXLINESTR must be compatible with operator '#' in C macro.
    325 #define MAXLINESTR 499
    326 // MAXLINE must be (MAXLINESTR + 1), representing the size of a C string
    327 // containing MAXLINESTR non-null chars plus a null.
    328 #define MAXLINE (MAXLINESTR + 1)
    329 #define MAKE_STR_HELPER(S) #S
    330 #define MAKE_STR(S) MAKE_STR_HELPER(S)
    331 #define EXPORT_VAR_STR "exportVarCount: "
    332 #define EXPORT_FUNC_STR "exportFuncCount: "
    333 #define EXPORT_FOREACH_STR "exportForEachCount: "
    334 #define EXPORT_REDUCE_STR "exportReduceCount: "
    335 #define OBJECT_SLOT_STR "objectSlotCount: "
    336 #define PRAGMA_STR "pragmaCount: "
    337 #define THREADABLE_STR "isThreadable: "
    338 #define CHECKSUM_STR "buildChecksum: "
    339 #define VERSIONINFO_STR "versionInfo: "
    340 
    341 // Copy up to a newline or size chars from str -> s, updating str
    342 // Returns s when successful and nullptr when '\0' is finally reached.
    343 static char* strgets(char *s, int size, const char **ppstr) {
    344     if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) {
    345         return nullptr;
    346     }
    347 
    348     int i;
    349     for (i = 0; i < (size - 1); i++) {
    350         s[i] = **ppstr;
    351         (*ppstr)++;
    352         if (s[i] == '\0') {
    353             return s;
    354         } else if (s[i] == '\n') {
    355             s[i+1] = '\0';
    356             return s;
    357         }
    358     }
    359 
    360     // size has been exceeded.
    361     s[i] = '\0';
    362 
    363     return s;
    364 }
    365 
    366 // Creates a duplicate of a string. The new string is as small as possible,
    367 // only including characters up to and including the first null-terminator;
    368 // otherwise, the new string will be the same size as the input string.
    369 // The code that calls duplicateString is responsible for the new string's
    370 // lifetime, and is responsible for freeing it when it is no longer needed.
    371 static char* duplicateString(const char *str, size_t length) {
    372     const size_t newLen = strnlen(str, length-1) + 1;
    373     char *newStr = new char[newLen];
    374     strlcpy(newStr, str, newLen);
    375     return newStr;
    376 }
    377 
    378 ScriptExecutable* ScriptExecutable::createFromSharedObject(
    379     void* sharedObj, uint32_t expectedChecksum) {
    380     char line[MAXLINE];
    381 
    382     size_t varCount = 0;
    383     size_t funcCount = 0;
    384     size_t forEachCount = 0;
    385     size_t reduceCount = 0;
    386     size_t objectSlotCount = 0;
    387     size_t pragmaCount = 0;
    388     bool isThreadable = true;
    389 
    390     void** fieldAddress = nullptr;
    391     bool* fieldIsObject = nullptr;
    392     char** fieldName = nullptr;
    393     InvokeFunc_t* invokeFunctions = nullptr;
    394     ForEachFunc_t* forEachFunctions = nullptr;
    395     uint32_t* forEachSignatures = nullptr;
    396     ReduceDescription* reduceDescriptions = nullptr;
    397     const char ** pragmaKeys = nullptr;
    398     const char ** pragmaValues = nullptr;
    399     uint32_t checksum = 0;
    400 
    401     const char *rsInfo = (const char *) dlsym(sharedObj, kRsInfo);
    402     int numEntries = 0;
    403     const int *rsGlobalEntries = (const int *) dlsym(sharedObj, kRsGlobalEntries);
    404     const char **rsGlobalNames = (const char **) dlsym(sharedObj, kRsGlobalNames);
    405     const void **rsGlobalAddresses = (const void **) dlsym(sharedObj, kRsGlobalAddresses);
    406     const size_t *rsGlobalSizes = (const size_t *) dlsym(sharedObj, kRsGlobalSizes);
    407     const uint32_t *rsGlobalProperties = (const uint32_t *) dlsym(sharedObj, kRsGlobalProperties);
    408 
    409     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    410         return nullptr;
    411     }
    412     if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
    413         ALOGE("Invalid export var count!: %s", line);
    414         return nullptr;
    415     }
    416 
    417     fieldAddress = new void*[varCount];
    418     if (fieldAddress == nullptr) {
    419         return nullptr;
    420     }
    421 
    422     fieldIsObject = new bool[varCount];
    423     if (fieldIsObject == nullptr) {
    424         goto error;
    425     }
    426 
    427     fieldName = new char*[varCount];
    428     if (fieldName == nullptr) {
    429         goto error;
    430     }
    431 
    432     for (size_t i = 0; i < varCount; ++i) {
    433         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    434             goto error;
    435         }
    436         char *c = strrchr(line, '\n');
    437         if (c) {
    438             *c = '\0';
    439         }
    440         void* addr = dlsym(sharedObj, line);
    441         if (addr == nullptr) {
    442             ALOGE("Failed to find variable address for %s: %s",
    443                   line, dlerror());
    444             // Not a critical error if we don't find a global variable.
    445         }
    446         fieldAddress[i] = addr;
    447         fieldIsObject[i] = false;
    448         fieldName[i] = duplicateString(line, sizeof(line));
    449     }
    450 
    451     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    452         goto error;
    453     }
    454     if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
    455         ALOGE("Invalid export func count!: %s", line);
    456         goto error;
    457     }
    458 
    459     invokeFunctions = new InvokeFunc_t[funcCount];
    460     if (invokeFunctions == nullptr) {
    461         goto error;
    462     }
    463 
    464     for (size_t i = 0; i < funcCount; ++i) {
    465         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    466             goto error;
    467         }
    468         char *c = strrchr(line, '\n');
    469         if (c) {
    470             *c = '\0';
    471         }
    472 
    473         invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
    474         if (invokeFunctions[i] == nullptr) {
    475             ALOGE("Failed to get function address for %s(): %s",
    476                   line, dlerror());
    477             goto error;
    478         }
    479     }
    480 
    481     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    482         goto error;
    483     }
    484     if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
    485         ALOGE("Invalid export forEach count!: %s", line);
    486         goto error;
    487     }
    488 
    489     forEachFunctions = new ForEachFunc_t[forEachCount];
    490     if (forEachFunctions == nullptr) {
    491         goto error;
    492     }
    493 
    494     forEachSignatures = new uint32_t[forEachCount];
    495     if (forEachSignatures == nullptr) {
    496         goto error;
    497     }
    498 
    499     for (size_t i = 0; i < forEachCount; ++i) {
    500         unsigned int tmpSig = 0;
    501         char tmpName[MAXLINE];
    502 
    503         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    504             goto error;
    505         }
    506         if (sscanf(line, "%u - %" MAKE_STR(MAXLINESTR) "s",
    507                    &tmpSig, tmpName) != 2) {
    508           ALOGE("Invalid export forEach!: %s", line);
    509           goto error;
    510         }
    511 
    512         // Lookup the expanded ForEach kernel.
    513         strncat(tmpName, ".expand", MAXLINESTR-strlen(tmpName));
    514         forEachSignatures[i] = tmpSig;
    515         forEachFunctions[i] =
    516             (ForEachFunc_t) dlsym(sharedObj, tmpName);
    517         if (i != 0 && forEachFunctions[i] == nullptr &&
    518             strcmp(tmpName, "root.expand")) {
    519             // Ignore missing root.expand functions.
    520             // root() is always specified at location 0.
    521             ALOGE("Failed to find forEach function address for %s(): %s",
    522                   tmpName, dlerror());
    523             goto error;
    524         }
    525     }
    526 
    527     // Read general reduce kernels
    528     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    529         goto error;
    530     }
    531     if (sscanf(line, EXPORT_REDUCE_STR "%zu", &reduceCount) != 1) {
    532         ALOGE("Invalid export reduce new count!: %s", line);
    533         goto error;
    534     }
    535 
    536     reduceDescriptions = new ReduceDescription[reduceCount];
    537     if (reduceDescriptions == nullptr) {
    538         goto error;
    539     }
    540 
    541     for (size_t i = 0; i < reduceCount; ++i) {
    542         static const char kNoName[] = ".";
    543 
    544         unsigned int tmpSig = 0;
    545         size_t tmpSize = 0;
    546         char tmpNameReduce[MAXLINE];
    547         char tmpNameInitializer[MAXLINE];
    548         char tmpNameAccumulator[MAXLINE];
    549         char tmpNameCombiner[MAXLINE];
    550         char tmpNameOutConverter[MAXLINE];
    551         char tmpNameHalter[MAXLINE];
    552 
    553         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    554             goto error;
    555         }
    556 #define DELIMNAME " - %" MAKE_STR(MAXLINESTR) "s"
    557         if (sscanf(line, "%u - %zu" DELIMNAME DELIMNAME DELIMNAME DELIMNAME DELIMNAME DELIMNAME,
    558                    &tmpSig, &tmpSize, tmpNameReduce, tmpNameInitializer, tmpNameAccumulator,
    559                    tmpNameCombiner, tmpNameOutConverter, tmpNameHalter) != 8) {
    560             ALOGE("Invalid export reduce new!: %s", line);
    561             goto error;
    562         }
    563 #undef DELIMNAME
    564 
    565         // For now, we expect
    566         // - Reduce and Accumulator names
    567         // - optional Initializer, Combiner, and OutConverter name
    568         // - no Halter name
    569         if (!strcmp(tmpNameReduce, kNoName) ||
    570             !strcmp(tmpNameAccumulator, kNoName)) {
    571             ALOGE("Expected reduce and accumulator names!: %s", line);
    572             goto error;
    573         }
    574         if (strcmp(tmpNameHalter, kNoName)) {
    575             ALOGE("Did not expect halter name!: %s", line);
    576             goto error;
    577         }
    578 
    579         // The current implementation does not use the signature
    580         // or reduce name.
    581 
    582         reduceDescriptions[i].accumSize = tmpSize;
    583 
    584         // Process the (optional) initializer.
    585         if (strcmp(tmpNameInitializer, kNoName)) {
    586           // Lookup the original user-written initializer.
    587           if (!(reduceDescriptions[i].initFunc =
    588                 (ReduceInitializerFunc_t) dlsym(sharedObj, tmpNameInitializer))) {
    589             ALOGE("Failed to find initializer function address for %s(): %s",
    590                   tmpNameInitializer, dlerror());
    591             goto error;
    592           }
    593         } else {
    594           reduceDescriptions[i].initFunc = nullptr;
    595         }
    596 
    597         // Lookup the expanded accumulator.
    598         strncat(tmpNameAccumulator, ".expand", MAXLINESTR-strlen(tmpNameAccumulator));
    599         if (!(reduceDescriptions[i].accumFunc =
    600               (ReduceAccumulatorFunc_t) dlsym(sharedObj, tmpNameAccumulator))) {
    601             ALOGE("Failed to find accumulator function address for %s(): %s",
    602                   tmpNameAccumulator, dlerror());
    603             goto error;
    604         }
    605 
    606         // Process the (optional) combiner.
    607         if (strcmp(tmpNameCombiner, kNoName)) {
    608           // Lookup the original user-written combiner.
    609           if (!(reduceDescriptions[i].combFunc =
    610                 (ReduceCombinerFunc_t) dlsym(sharedObj, tmpNameCombiner))) {
    611             ALOGE("Failed to find combiner function address for %s(): %s",
    612                   tmpNameCombiner, dlerror());
    613             goto error;
    614           }
    615         } else {
    616           reduceDescriptions[i].combFunc = nullptr;
    617         }
    618 
    619         // Process the (optional) outconverter.
    620         if (strcmp(tmpNameOutConverter, kNoName)) {
    621           // Lookup the original user-written outconverter.
    622           if (!(reduceDescriptions[i].outFunc =
    623                 (ReduceOutConverterFunc_t) dlsym(sharedObj, tmpNameOutConverter))) {
    624             ALOGE("Failed to find outconverter function address for %s(): %s",
    625                   tmpNameOutConverter, dlerror());
    626             goto error;
    627           }
    628         } else {
    629           reduceDescriptions[i].outFunc = nullptr;
    630         }
    631     }
    632 
    633     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    634         goto error;
    635     }
    636     if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
    637         ALOGE("Invalid object slot count!: %s", line);
    638         goto error;
    639     }
    640 
    641     for (size_t i = 0; i < objectSlotCount; ++i) {
    642         uint32_t varNum = 0;
    643         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    644             goto error;
    645         }
    646         if (sscanf(line, "%u", &varNum) != 1) {
    647             ALOGE("Invalid object slot!: %s", line);
    648             goto error;
    649         }
    650 
    651         if (varNum < varCount) {
    652             fieldIsObject[varNum] = true;
    653         }
    654     }
    655 
    656 #ifndef RS_COMPATIBILITY_LIB
    657     // Do not attempt to read pragmas or isThreadable flag in compat lib path.
    658     // Neither is applicable for compat lib
    659 
    660     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    661         goto error;
    662     }
    663 
    664     if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
    665         ALOGE("Invalid pragma count!: %s", line);
    666         goto error;
    667     }
    668 
    669     pragmaKeys = new const char*[pragmaCount];
    670     if (pragmaKeys == nullptr) {
    671         goto error;
    672     }
    673 
    674     pragmaValues = new const char*[pragmaCount];
    675     if (pragmaValues == nullptr) {
    676         goto error;
    677     }
    678 
    679     bzero(pragmaKeys, sizeof(char*) * pragmaCount);
    680     bzero(pragmaValues, sizeof(char*) * pragmaCount);
    681 
    682     for (size_t i = 0; i < pragmaCount; ++i) {
    683         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    684             ALOGE("Unable to read pragma at index %zu!", i);
    685             goto error;
    686         }
    687         char key[MAXLINE];
    688         char value[MAXLINE] = ""; // initialize in case value is empty
    689 
    690         // pragmas can just have a key and no value.  Only check to make sure
    691         // that the key is not empty
    692         if (sscanf(line, "%" MAKE_STR(MAXLINESTR) "s - %" MAKE_STR(MAXLINESTR) "s",
    693                    key, value) == 0 ||
    694             strlen(key) == 0)
    695         {
    696             ALOGE("Invalid pragma value!: %s", line);
    697 
    698             goto error;
    699         }
    700 
    701         pragmaKeys[i] = duplicateString(key, sizeof(key));
    702         pragmaValues[i] = duplicateString(value, sizeof(value));
    703         //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
    704     }
    705 
    706     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
    707         goto error;
    708     }
    709 
    710     char tmpFlag[4];
    711     if (sscanf(line, THREADABLE_STR "%3s", tmpFlag) != 1) {
    712         ALOGE("Invalid threadable flag!: %s", line);
    713         goto error;
    714     }
    715     if (strcmp(tmpFlag, "yes") == 0) {
    716         isThreadable = true;
    717     } else if (strcmp(tmpFlag, "no") == 0) {
    718         isThreadable = false;
    719     } else {
    720         ALOGE("Invalid threadable flag!: %s", tmpFlag);
    721         goto error;
    722     }
    723 
    724     if (strgets(line, MAXLINE, &rsInfo) != nullptr) {
    725         if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) {
    726             ALOGE("Invalid checksum flag!: %s", line);
    727             goto error;
    728         }
    729     } else {
    730         ALOGE("Missing checksum in shared obj file");
    731         goto error;
    732     }
    733 
    734     if (expectedChecksum != 0 && checksum != expectedChecksum) {
    735         ALOGE("Found invalid checksum.  Expected %08x, got %08x\n",
    736               expectedChecksum, checksum);
    737         goto error;
    738     }
    739 
    740     {
    741       // Parse the version info string, but ignore its contents as it's only
    742       // used by the debugger
    743       size_t nLines = 0;
    744       if (strgets(line, MAXLINE, &rsInfo) != nullptr) {
    745         if (sscanf(line, VERSIONINFO_STR "%zu", &nLines) != 1) {
    746           ALOGE("invalid versionInfo count");
    747           goto error;
    748         } else {
    749           // skip the versionInfo packet as libRs doesn't use it
    750           while (nLines) {
    751             --nLines;
    752             if (strgets(line, MAXLINE, &rsInfo) == nullptr)
    753               goto error;
    754           }
    755         }
    756       } else {
    757         ALOGE(".rs.info is missing versionInfo section");
    758       }
    759     }
    760 
    761 #endif  // RS_COMPATIBILITY_LIB
    762 
    763     // Read in information about mutable global variables provided by bcc's
    764     // RSGlobalInfoPass
    765     if (rsGlobalEntries) {
    766         numEntries = *rsGlobalEntries;
    767         if (numEntries > 0) {
    768             rsAssert(rsGlobalNames);
    769             rsAssert(rsGlobalAddresses);
    770             rsAssert(rsGlobalSizes);
    771             rsAssert(rsGlobalProperties);
    772         }
    773     }
    774 
    775     return new ScriptExecutable(
    776         fieldAddress, fieldIsObject, fieldName, varCount,
    777         invokeFunctions, funcCount,
    778         forEachFunctions, forEachSignatures, forEachCount,
    779         reduceDescriptions, reduceCount,
    780         pragmaKeys, pragmaValues, pragmaCount,
    781         rsGlobalNames, rsGlobalAddresses, rsGlobalSizes, rsGlobalProperties,
    782         numEntries, isThreadable, checksum);
    783 
    784 error:
    785 
    786 #ifndef RS_COMPATIBILITY_LIB
    787 
    788     if (pragmaKeys) {
    789         for (size_t idx = 0; idx < pragmaCount; ++idx) {
    790             delete [] pragmaKeys[idx];
    791         }
    792     }
    793 
    794     if (pragmaValues) {
    795         for (size_t idx = 0; idx < pragmaCount; ++idx) {
    796             delete [] pragmaValues[idx];
    797         }
    798     }
    799 
    800     delete[] pragmaValues;
    801     delete[] pragmaKeys;
    802 #endif  // RS_COMPATIBILITY_LIB
    803 
    804     delete[] reduceDescriptions;
    805 
    806     delete[] forEachSignatures;
    807     delete[] forEachFunctions;
    808 
    809     delete[] invokeFunctions;
    810 
    811     for (size_t i = 0; i < varCount; i++) {
    812         delete[] fieldName[i];
    813     }
    814     delete[] fieldName;
    815     delete[] fieldIsObject;
    816     delete[] fieldAddress;
    817 
    818     return nullptr;
    819 }
    820 
    821 void* ScriptExecutable::getFieldAddress(const char* name) const {
    822     // TODO: improve this by using a hash map.
    823     for (size_t i = 0; i < mExportedVarCount; i++) {
    824         if (strcmp(name, mFieldName[i]) == 0) {
    825             return mFieldAddress[i];
    826         }
    827     }
    828     return nullptr;
    829 }
    830 
    831 bool ScriptExecutable::dumpGlobalInfo() const {
    832     ALOGE("Globals: %p %p %p", mGlobalAddresses, mGlobalSizes, mGlobalNames);
    833     ALOGE("P   - Pointer");
    834     ALOGE(" C  - Constant");
    835     ALOGE("  S - Static");
    836     for (int i = 0; i < mGlobalEntries; i++) {
    837         ALOGE("Global[%d]: %p %zu %s", i, mGlobalAddresses[i], mGlobalSizes[i],
    838               mGlobalNames[i]);
    839         uint32_t properties = mGlobalProperties[i];
    840         ALOGE("%c%c%c Type: %u",
    841               isGlobalPointer(properties)  ? 'P' : ' ',
    842               isGlobalConstant(properties) ? 'C' : ' ',
    843               isGlobalStatic(properties)   ? 'S' : ' ',
    844               getGlobalRsType(properties));
    845     }
    846     return true;
    847 }
    848 
    849 }  // namespace renderscript
    850 }  // namespace android
    851