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