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