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