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    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
170const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
171
172void* 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
187void* 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
228std::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
248void* 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.
339static 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.
367static 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
374ScriptExecutable* 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
779error:
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
816void* 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
826bool 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