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