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