• 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  #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