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