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
20namespace android {
21namespace renderscript {
22
23namespace {
24
25// Check if a path exists and attempt to create it if it doesn't.
26static 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.
39static 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
62static 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
98static 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
115const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc";
116const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache";
117
118#ifndef RS_COMPATIBILITY_LIB
119
120bool 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
174const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
175
176void* 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
191void* 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
232std::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
252void* 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.
343static 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.
371static 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
378ScriptExecutable* 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
784error:
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
821void* 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
831bool 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