1 #include "rsCpuExecutable.h"
2 #include "rsCppUtils.h"
3 
4 #include <fstream>
5 #include <set>
6 #include <memory>
7 
8 #ifdef RS_COMPATIBILITY_LIB
9 #include <stdio.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12 #else
13 #include "bcc/Config/Config.h"
14 #endif
15 
16 #include <dlfcn.h>
17 
18 namespace android {
19 namespace renderscript {
20 
21 namespace {
22 
23 // Check if a path exists and attempt to create it if it doesn't.
ensureCacheDirExists(const char * path)24 static bool ensureCacheDirExists(const char *path) {
25     if (access(path, R_OK | W_OK | X_OK) == 0) {
26         // Done if we can rwx the directory
27         return true;
28     }
29     if (mkdir(path, 0700) == 0) {
30         return true;
31     }
32     return false;
33 }
34 
35 // Copy the file named \p srcFile to \p dstFile.
36 // Return 0 on success and -1 if anything wasn't copied.
copyFile(const char * dstFile,const char * srcFile)37 static int copyFile(const char *dstFile, const char *srcFile) {
38     std::ifstream srcStream(srcFile);
39     if (!srcStream) {
40         ALOGE("Could not verify or read source file: %s", srcFile);
41         return -1;
42     }
43     std::ofstream dstStream(dstFile);
44     if (!dstStream) {
45         ALOGE("Could not verify or write destination file: %s", dstFile);
46         return -1;
47     }
48     dstStream << srcStream.rdbuf();
49     if (!dstStream) {
50         ALOGE("Could not write destination file: %s", dstFile);
51         return -1;
52     }
53 
54     srcStream.close();
55     dstStream.close();
56 
57     return 0;
58 }
59 
findSharedObjectName(const char * cacheDir,const char * resName)60 static std::string findSharedObjectName(const char *cacheDir,
61                                         const char *resName) {
62 #ifndef RS_SERVER
63     std::string scriptSOName(cacheDir);
64 #if defined(RS_COMPATIBILITY_LIB) && !defined(__LP64__)
65     size_t cutPos = scriptSOName.rfind("cache");
66     if (cutPos != std::string::npos) {
67         scriptSOName.erase(cutPos);
68     } else {
69         ALOGE("Found peculiar cacheDir (missing \"cache\"): %s", cacheDir);
70     }
71     scriptSOName.append("/lib/librs.");
72 #else
73     scriptSOName.append("/librs.");
74 #endif // RS_COMPATIBILITY_LIB
75 
76 #else
77     std::string scriptSOName("lib");
78 #endif // RS_SERVER
79     scriptSOName.append(resName);
80     scriptSOName.append(".so");
81 
82     return scriptSOName;
83 }
84 
85 }  // anonymous namespace
86 
87 const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc";
88 const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache";
89 
90 #ifndef RS_COMPATIBILITY_LIB
91 
createSharedLibrary(const char * driverName,const char * cacheDir,const char * resName)92 bool SharedLibraryUtils::createSharedLibrary(const char *driverName,
93                                              const char *cacheDir,
94                                              const char *resName) {
95     std::string sharedLibName = findSharedObjectName(cacheDir, resName);
96     std::string objFileName = cacheDir;
97     objFileName.append("/");
98     objFileName.append(resName);
99     objFileName.append(".o");
100     // Should be something like "libRSDriver.so".
101     std::string linkDriverName = driverName;
102     // Remove ".so" and replace "lib" with "-l".
103     // This will leave us with "-lRSDriver" instead.
104     linkDriverName.erase(linkDriverName.length() - 3);
105     linkDriverName.replace(0, 3, "-l");
106 
107     const char *compiler_rt = SYSLIBPATH"/libcompiler_rt.so";
108     const char *mTriple = "-mtriple=" DEFAULT_TARGET_TRIPLE_STRING;
109     const char *libPath = "--library-path=" SYSLIBPATH;
110     const char *vendorLibPath = "--library-path=" SYSLIBPATH_VENDOR;
111 
112     std::vector<const char *> args = {
113         LD_EXE_PATH,
114         "-shared",
115         "-nostdlib",
116         compiler_rt, mTriple, vendorLibPath, libPath,
117         linkDriverName.c_str(), "-lm", "-lc",
118         objFileName.c_str(),
119         "-o", sharedLibName.c_str(),
120         nullptr
121     };
122 
123     return rsuExecuteCommand(LD_EXE_PATH, args.size()-1, args.data());
124 
125 }
126 
127 #endif  // RS_COMPATIBILITY_LIB
128 
129 const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
130 
loadSharedLibrary(const char * cacheDir,const char * resName,const char * nativeLibDir,bool * alreadyLoaded)131 void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir,
132                                             const char *resName,
133                                             const char *nativeLibDir,
134                                             bool* alreadyLoaded) {
135     void *loaded = nullptr;
136 
137 #if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
138     std::string scriptSOName = findSharedObjectName(nativeLibDir, resName);
139 #else
140     std::string scriptSOName = findSharedObjectName(cacheDir, resName);
141 #endif
142 
143     // We should check if we can load the library from the standard app
144     // location for shared libraries first.
145     loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName, alreadyLoaded);
146 
147     if (loaded == nullptr) {
148         ALOGE("Unable to open shared library (%s): %s",
149               scriptSOName.c_str(), dlerror());
150 
151 #ifdef RS_COMPATIBILITY_LIB
152         // One final attempt to find the library in "/system/lib".
153         // We do this to allow bundled applications to use the compatibility
154         // library fallback path. Those applications don't have a private
155         // library path, so they need to install to the system directly.
156         // Note that this is really just a testing path.
157         std::string scriptSONameSystem("/system/lib/librs.");
158         scriptSONameSystem.append(resName);
159         scriptSONameSystem.append(".so");
160         loaded = loadSOHelper(scriptSONameSystem.c_str(), cacheDir,
161                               resName);
162         if (loaded == nullptr) {
163             ALOGE("Unable to open system shared library (%s): %s",
164                   scriptSONameSystem.c_str(), dlerror());
165         }
166 #endif
167     }
168 
169     return loaded;
170 }
171 
getRandomString(size_t len)172 String8 SharedLibraryUtils::getRandomString(size_t len) {
173     char buf[len + 1];
174     for (size_t i = 0; i < len; i++) {
175         uint32_t r = arc4random() & 0xffff;
176         r %= 62;
177         if (r < 26) {
178             // lowercase
179             buf[i] = 'a' + r;
180         } else if (r < 52) {
181             // uppercase
182             buf[i] = 'A' + (r - 26);
183         } else {
184             // Use a number
185             buf[i] = '0' + (r - 52);
186         }
187     }
188     buf[len] = '\0';
189     return String8(buf);
190 }
191 
loadSOHelper(const char * origName,const char * cacheDir,const char * resName,bool * alreadyLoaded)192 void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
193                                        const char *resName, bool *alreadyLoaded) {
194     // Keep track of which .so libraries have been loaded. Once a library is
195     // in the set (per-process granularity), we must instead make a copy of
196     // the original shared object (randomly named .so file) and load that one
197     // instead. If we don't do this, we end up aliasing global data between
198     // the various Script instances (which are supposed to be completely
199     // independent).
200     static std::set<std::string> LoadedLibraries;
201 
202     void *loaded = nullptr;
203 
204     // Skip everything if we don't even have the original library available.
205     if (access(origName, F_OK) != 0) {
206         return nullptr;
207     }
208 
209     // Common path is that we have not loaded this Script/library before.
210     if (LoadedLibraries.find(origName) == LoadedLibraries.end()) {
211         if (alreadyLoaded != nullptr) {
212             *alreadyLoaded = false;
213         }
214         loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
215         if (loaded) {
216             LoadedLibraries.insert(origName);
217         }
218         return loaded;
219     }
220 
221     if (alreadyLoaded != nullptr) {
222         *alreadyLoaded = true;
223     }
224 
225     std::string newName(cacheDir);
226 
227     // Append RS_CACHE_DIR only if it is not found in cacheDir
228     // In driver mode, RS_CACHE_DIR is already appended to cacheDir.
229     if (newName.find(RS_CACHE_DIR) == std::string::npos) {
230         newName.append("/");
231         newName.append(RS_CACHE_DIR);
232         newName.append("/");
233     }
234 
235     if (!ensureCacheDirExists(newName.c_str())) {
236         ALOGE("Could not verify or create cache dir: %s", cacheDir);
237         return nullptr;
238     }
239 
240     // Construct an appropriately randomized filename for the copy.
241     newName.append("librs.");
242     newName.append(resName);
243     newName.append("#");
244     newName.append(getRandomString(6).string());  // 62^6 potential filename variants.
245     newName.append(".so");
246 
247     int r = copyFile(newName.c_str(), origName);
248     if (r != 0) {
249         ALOGE("Could not create copy %s -> %s", origName, newName.c_str());
250         return nullptr;
251     }
252     loaded = dlopen(newName.c_str(), RTLD_NOW | RTLD_LOCAL);
253     r = unlink(newName.c_str());
254     if (r != 0) {
255         ALOGE("Could not unlink copy %s", newName.c_str());
256     }
257     if (loaded) {
258         LoadedLibraries.insert(newName.c_str());
259     }
260 
261     return loaded;
262 }
263 
264 #define MAXLINE 500
265 #define MAKE_STR_HELPER(S) #S
266 #define MAKE_STR(S) MAKE_STR_HELPER(S)
267 #define EXPORT_VAR_STR "exportVarCount: "
268 #define EXPORT_FUNC_STR "exportFuncCount: "
269 #define EXPORT_FOREACH_STR "exportForEachCount: "
270 #define OBJECT_SLOT_STR "objectSlotCount: "
271 #define PRAGMA_STR "pragmaCount: "
272 #define THREADABLE_STR "isThreadable: "
273 #define CHECKSUM_STR "buildChecksum: "
274 
275 // Copy up to a newline or size chars from str -> s, updating str
276 // Returns s when successful and nullptr when '\0' is finally reached.
strgets(char * s,int size,const char ** ppstr)277 static char* strgets(char *s, int size, const char **ppstr) {
278     if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) {
279         return nullptr;
280     }
281 
282     int i;
283     for (i = 0; i < (size - 1); i++) {
284         s[i] = **ppstr;
285         (*ppstr)++;
286         if (s[i] == '\0') {
287             return s;
288         } else if (s[i] == '\n') {
289             s[i+1] = '\0';
290             return s;
291         }
292     }
293 
294     // size has been exceeded.
295     s[i] = '\0';
296 
297     return s;
298 }
299 
createFromSharedObject(Context * RSContext,void * sharedObj,uint32_t expectedChecksum)300 ScriptExecutable* ScriptExecutable::createFromSharedObject(
301     Context* RSContext, void* sharedObj, uint32_t expectedChecksum) {
302     char line[MAXLINE];
303 
304     size_t varCount = 0;
305     size_t funcCount = 0;
306     size_t forEachCount = 0;
307     size_t objectSlotCount = 0;
308     size_t pragmaCount = 0;
309     bool isThreadable = true;
310 
311     void** fieldAddress = nullptr;
312     bool* fieldIsObject = nullptr;
313     char** fieldName = nullptr;
314     InvokeFunc_t* invokeFunctions = nullptr;
315     ForEachFunc_t* forEachFunctions = nullptr;
316     uint32_t* forEachSignatures = nullptr;
317     const char ** pragmaKeys = nullptr;
318     const char ** pragmaValues = nullptr;
319     uint32_t checksum = 0;
320 
321     const char *rsInfo = (const char *) dlsym(sharedObj, kRsInfo);
322     int numEntries = 0;
323     const int *rsGlobalEntries = (const int *) dlsym(sharedObj, kRsGlobalEntries);
324     const char **rsGlobalNames = (const char **) dlsym(sharedObj, kRsGlobalNames);
325     const void **rsGlobalAddresses = (const void **) dlsym(sharedObj, kRsGlobalAddresses);
326     const size_t *rsGlobalSizes = (const size_t *) dlsym(sharedObj, kRsGlobalSizes);
327     const uint32_t *rsGlobalProperties = (const uint32_t *) dlsym(sharedObj, kRsGlobalProperties);
328 
329     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
330         return nullptr;
331     }
332     if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
333         ALOGE("Invalid export var count!: %s", line);
334         return nullptr;
335     }
336 
337     fieldAddress = new void*[varCount];
338     if (fieldAddress == nullptr) {
339         return nullptr;
340     }
341 
342     fieldIsObject = new bool[varCount];
343     if (fieldIsObject == nullptr) {
344         goto error;
345     }
346 
347     fieldName = new char*[varCount];
348     if (fieldName == nullptr) {
349         goto error;
350     }
351 
352     for (size_t i = 0; i < varCount; ++i) {
353         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
354             goto error;
355         }
356         char *c = strrchr(line, '\n');
357         if (c) {
358             *c = '\0';
359         }
360         void* addr = dlsym(sharedObj, line);
361         if (addr == nullptr) {
362             ALOGE("Failed to find variable address for %s: %s",
363                   line, dlerror());
364             // Not a critical error if we don't find a global variable.
365         }
366         fieldAddress[i] = addr;
367         fieldIsObject[i] = false;
368         fieldName[i] = new char[strlen(line)+1];
369         strcpy(fieldName[i], line);
370     }
371 
372     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
373         goto error;
374     }
375     if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
376         ALOGE("Invalid export func count!: %s", line);
377         goto error;
378     }
379 
380     invokeFunctions = new InvokeFunc_t[funcCount];
381     if (invokeFunctions == nullptr) {
382         goto error;
383     }
384 
385     for (size_t i = 0; i < funcCount; ++i) {
386         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
387             goto error;
388         }
389         char *c = strrchr(line, '\n');
390         if (c) {
391             *c = '\0';
392         }
393 
394         invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
395         if (invokeFunctions[i] == nullptr) {
396             ALOGE("Failed to get function address for %s(): %s",
397                   line, dlerror());
398             goto error;
399         }
400     }
401 
402     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
403         goto error;
404     }
405     if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
406         ALOGE("Invalid export forEach count!: %s", line);
407         goto error;
408     }
409 
410     forEachFunctions = new ForEachFunc_t[forEachCount];
411     if (forEachFunctions == nullptr) {
412         goto error;
413     }
414 
415     forEachSignatures = new uint32_t[forEachCount];
416     if (forEachSignatures == nullptr) {
417         goto error;
418     }
419 
420     for (size_t i = 0; i < forEachCount; ++i) {
421         unsigned int tmpSig = 0;
422         char tmpName[MAXLINE];
423 
424         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
425             goto error;
426         }
427         if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s",
428                    &tmpSig, tmpName) != 2) {
429           ALOGE("Invalid export forEach!: %s", line);
430           goto error;
431         }
432 
433         // Lookup the expanded ForEach kernel.
434         strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName));
435         forEachSignatures[i] = tmpSig;
436         forEachFunctions[i] =
437             (ForEachFunc_t) dlsym(sharedObj, tmpName);
438         if (i != 0 && forEachFunctions[i] == nullptr &&
439             strcmp(tmpName, "root.expand")) {
440             // Ignore missing root.expand functions.
441             // root() is always specified at location 0.
442             ALOGE("Failed to find forEach function address for %s: %s",
443                   tmpName, dlerror());
444             goto error;
445         }
446     }
447 
448     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
449         goto error;
450     }
451     if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
452         ALOGE("Invalid object slot count!: %s", line);
453         goto error;
454     }
455 
456     for (size_t i = 0; i < objectSlotCount; ++i) {
457         uint32_t varNum = 0;
458         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
459             goto error;
460         }
461         if (sscanf(line, "%u", &varNum) != 1) {
462             ALOGE("Invalid object slot!: %s", line);
463             goto error;
464         }
465 
466         if (varNum < varCount) {
467             fieldIsObject[varNum] = true;
468         }
469     }
470 
471 #ifndef RS_COMPATIBILITY_LIB
472     // Do not attempt to read pragmas or isThreadable flag in compat lib path.
473     // Neither is applicable for compat lib
474 
475     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
476         goto error;
477     }
478 
479     if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
480         ALOGE("Invalid pragma count!: %s", line);
481         goto error;
482     }
483 
484     pragmaKeys = new const char*[pragmaCount];
485     if (pragmaKeys == nullptr) {
486         goto error;
487     }
488 
489     pragmaValues = new const char*[pragmaCount];
490     if (pragmaValues == nullptr) {
491         goto error;
492     }
493 
494     bzero(pragmaKeys, sizeof(char*) * pragmaCount);
495     bzero(pragmaValues, sizeof(char*) * pragmaCount);
496 
497     for (size_t i = 0; i < pragmaCount; ++i) {
498         if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
499             ALOGE("Unable to read pragma at index %zu!", i);
500             goto error;
501         }
502         char key[MAXLINE];
503         char value[MAXLINE] = ""; // initialize in case value is empty
504 
505         // pragmas can just have a key and no value.  Only check to make sure
506         // that the key is not empty
507         if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s",
508                    key, value) == 0 ||
509             strlen(key) == 0)
510         {
511             ALOGE("Invalid pragma value!: %s", line);
512 
513             goto error;
514         }
515 
516         char *pKey = new char[strlen(key)+1];
517         strcpy(pKey, key);
518         pragmaKeys[i] = pKey;
519 
520         char *pValue = new char[strlen(value)+1];
521         strcpy(pValue, value);
522         pragmaValues[i] = pValue;
523         //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
524     }
525 
526     if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
527         goto error;
528     }
529 
530     char tmpFlag[4];
531     if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) {
532         ALOGE("Invalid threadable flag!: %s", line);
533         goto error;
534     }
535     if (strcmp(tmpFlag, "yes") == 0) {
536         isThreadable = true;
537     } else if (strcmp(tmpFlag, "no") == 0) {
538         isThreadable = false;
539     } else {
540         ALOGE("Invalid threadable flag!: %s", tmpFlag);
541         goto error;
542     }
543 
544     if (strgets(line, MAXLINE, &rsInfo) != nullptr) {
545         if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) {
546             ALOGE("Invalid checksum flag!: %s", line);
547             goto error;
548         }
549     } else {
550         ALOGE("Missing checksum in shared obj file");
551         goto error;
552     }
553 
554     if (expectedChecksum != 0 && checksum != expectedChecksum) {
555         ALOGE("Found invalid checksum.  Expected %08x, got %08x\n",
556               expectedChecksum, checksum);
557         goto error;
558     }
559 
560 #endif  // RS_COMPATIBILITY_LIB
561 
562     // Read in information about mutable global variables provided by bcc's
563     // RSGlobalInfoPass
564     if (rsGlobalEntries) {
565         numEntries = *rsGlobalEntries;
566         if (numEntries > 0) {
567             rsAssert(rsGlobalNames);
568             rsAssert(rsGlobalAddresses);
569             rsAssert(rsGlobalSizes);
570             rsAssert(rsGlobalProperties);
571         }
572     } else {
573         ALOGD("Missing .rs.global_entries from shared object");
574     }
575 
576     return new ScriptExecutable(
577         RSContext, fieldAddress, fieldIsObject, fieldName, varCount,
578         invokeFunctions, funcCount,
579         forEachFunctions, forEachSignatures, forEachCount,
580         pragmaKeys, pragmaValues, pragmaCount,
581         rsGlobalNames, rsGlobalAddresses, rsGlobalSizes, rsGlobalProperties,
582         numEntries, isThreadable, checksum);
583 
584 error:
585 
586 #ifndef RS_COMPATIBILITY_LIB
587 
588     for (size_t idx = 0; idx < pragmaCount; ++idx) {
589         delete [] pragmaKeys[idx];
590         delete [] pragmaValues[idx];
591     }
592 
593     delete[] pragmaValues;
594     delete[] pragmaKeys;
595 #endif  // RS_COMPATIBILITY_LIB
596 
597     delete[] forEachSignatures;
598     delete[] forEachFunctions;
599 
600     delete[] invokeFunctions;
601 
602     for (size_t i = 0; i < varCount; i++) {
603         delete[] fieldName[i];
604     }
605     delete[] fieldName;
606     delete[] fieldIsObject;
607     delete[] fieldAddress;
608 
609     return nullptr;
610 }
611 
getFieldAddress(const char * name) const612 void* ScriptExecutable::getFieldAddress(const char* name) const {
613     // TODO: improve this by using a hash map.
614     for (size_t i = 0; i < mExportedVarCount; i++) {
615         if (strcmp(name, mFieldName[i]) == 0) {
616             return mFieldAddress[i];
617         }
618     }
619     return nullptr;
620 }
621 
dumpGlobalInfo() const622 bool ScriptExecutable::dumpGlobalInfo() const {
623     ALOGE("Globals: %p %p %p", mGlobalAddresses, mGlobalSizes, mGlobalNames);
624     ALOGE("P   - Pointer");
625     ALOGE(" C  - Constant");
626     ALOGE("  S - Static");
627     for (int i = 0; i < mGlobalEntries; i++) {
628         ALOGE("Global[%d]: %p %zu %s", i, mGlobalAddresses[i], mGlobalSizes[i],
629               mGlobalNames[i]);
630         uint32_t properties = mGlobalProperties[i];
631         ALOGE("%c%c%c Type: %u",
632               isGlobalPointer(properties)  ? 'P' : ' ',
633               isGlobalConstant(properties) ? 'C' : ' ',
634               isGlobalStatic(properties)   ? 'S' : ' ',
635               getGlobalRsType(properties));
636     }
637     return true;
638 }
639 
640 }  // namespace renderscript
641 }  // namespace android
642