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