rsCpuExecutable.cpp revision 14ce007a633b10e3b9a3fae29d8f53a7e8c9b59f
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 18namespace android { 19namespace renderscript { 20 21namespace { 22 23// Check if a path exists and attempt to create it if it doesn't. 24static 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. 37static 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 60static 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 87const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc"; 88const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache"; 89 90#ifndef RS_COMPATIBILITY_LIB 91 92bool 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 129const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc"; 130 131void* 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 172String8 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 192void* 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 EXPORT_REDUCE_STR "exportReduceCount: " 271#define OBJECT_SLOT_STR "objectSlotCount: " 272#define PRAGMA_STR "pragmaCount: " 273#define THREADABLE_STR "isThreadable: " 274#define CHECKSUM_STR "buildChecksum: " 275 276// Copy up to a newline or size chars from str -> s, updating str 277// Returns s when successful and nullptr when '\0' is finally reached. 278static char* strgets(char *s, int size, const char **ppstr) { 279 if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) { 280 return nullptr; 281 } 282 283 int i; 284 for (i = 0; i < (size - 1); i++) { 285 s[i] = **ppstr; 286 (*ppstr)++; 287 if (s[i] == '\0') { 288 return s; 289 } else if (s[i] == '\n') { 290 s[i+1] = '\0'; 291 return s; 292 } 293 } 294 295 // size has been exceeded. 296 s[i] = '\0'; 297 298 return s; 299} 300 301ScriptExecutable* ScriptExecutable::createFromSharedObject( 302 Context* RSContext, void* sharedObj, uint32_t expectedChecksum) { 303 char line[MAXLINE]; 304 305 size_t varCount = 0; 306 size_t funcCount = 0; 307 size_t forEachCount = 0; 308 size_t reduceCount = 0; 309 size_t objectSlotCount = 0; 310 size_t pragmaCount = 0; 311 bool isThreadable = true; 312 313 void** fieldAddress = nullptr; 314 bool* fieldIsObject = nullptr; 315 char** fieldName = nullptr; 316 InvokeFunc_t* invokeFunctions = nullptr; 317 ForEachFunc_t* forEachFunctions = nullptr; 318 uint32_t* forEachSignatures = nullptr; 319 ReduceFunc_t* reduceFunctions = nullptr; 320 const char ** pragmaKeys = nullptr; 321 const char ** pragmaValues = nullptr; 322 uint32_t checksum = 0; 323 324 const char *rsInfo = (const char *) dlsym(sharedObj, kRsInfo); 325 int numEntries = 0; 326 const int *rsGlobalEntries = (const int *) dlsym(sharedObj, kRsGlobalEntries); 327 const char **rsGlobalNames = (const char **) dlsym(sharedObj, kRsGlobalNames); 328 const void **rsGlobalAddresses = (const void **) dlsym(sharedObj, kRsGlobalAddresses); 329 const size_t *rsGlobalSizes = (const size_t *) dlsym(sharedObj, kRsGlobalSizes); 330 const uint32_t *rsGlobalProperties = (const uint32_t *) dlsym(sharedObj, kRsGlobalProperties); 331 332 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 333 return nullptr; 334 } 335 if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) { 336 ALOGE("Invalid export var count!: %s", line); 337 return nullptr; 338 } 339 340 fieldAddress = new void*[varCount]; 341 if (fieldAddress == nullptr) { 342 return nullptr; 343 } 344 345 fieldIsObject = new bool[varCount]; 346 if (fieldIsObject == nullptr) { 347 goto error; 348 } 349 350 fieldName = new char*[varCount]; 351 if (fieldName == nullptr) { 352 goto error; 353 } 354 355 for (size_t i = 0; i < varCount; ++i) { 356 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 357 goto error; 358 } 359 char *c = strrchr(line, '\n'); 360 if (c) { 361 *c = '\0'; 362 } 363 void* addr = dlsym(sharedObj, line); 364 if (addr == nullptr) { 365 ALOGE("Failed to find variable address for %s: %s", 366 line, dlerror()); 367 // Not a critical error if we don't find a global variable. 368 } 369 fieldAddress[i] = addr; 370 fieldIsObject[i] = false; 371 fieldName[i] = new char[strlen(line)+1]; 372 strcpy(fieldName[i], line); 373 } 374 375 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 376 goto error; 377 } 378 if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) { 379 ALOGE("Invalid export func count!: %s", line); 380 goto error; 381 } 382 383 invokeFunctions = new InvokeFunc_t[funcCount]; 384 if (invokeFunctions == nullptr) { 385 goto error; 386 } 387 388 for (size_t i = 0; i < funcCount; ++i) { 389 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 390 goto error; 391 } 392 char *c = strrchr(line, '\n'); 393 if (c) { 394 *c = '\0'; 395 } 396 397 invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line); 398 if (invokeFunctions[i] == nullptr) { 399 ALOGE("Failed to get function address for %s(): %s", 400 line, dlerror()); 401 goto error; 402 } 403 } 404 405 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 406 goto error; 407 } 408 if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) { 409 ALOGE("Invalid export forEach count!: %s", line); 410 goto error; 411 } 412 413 forEachFunctions = new ForEachFunc_t[forEachCount]; 414 if (forEachFunctions == nullptr) { 415 goto error; 416 } 417 418 forEachSignatures = new uint32_t[forEachCount]; 419 if (forEachSignatures == nullptr) { 420 goto error; 421 } 422 423 for (size_t i = 0; i < forEachCount; ++i) { 424 unsigned int tmpSig = 0; 425 char tmpName[MAXLINE]; 426 427 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 428 goto error; 429 } 430 if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s", 431 &tmpSig, tmpName) != 2) { 432 ALOGE("Invalid export forEach!: %s", line); 433 goto error; 434 } 435 436 // Lookup the expanded ForEach kernel. 437 strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName)); 438 forEachSignatures[i] = tmpSig; 439 forEachFunctions[i] = 440 (ForEachFunc_t) dlsym(sharedObj, tmpName); 441 if (i != 0 && forEachFunctions[i] == nullptr && 442 strcmp(tmpName, "root.expand")) { 443 // Ignore missing root.expand functions. 444 // root() is always specified at location 0. 445 ALOGE("Failed to find forEach function address for %s(): %s", 446 tmpName, dlerror()); 447 goto error; 448 } 449 } 450 451 // Read reduce kernels 452 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 453 goto error; 454 } 455 if (sscanf(line, EXPORT_REDUCE_STR "%zu", &reduceCount) != 1) { 456 ALOGE("Invalid export reduce count!: %s", line); 457 goto error; 458 } 459 460 reduceFunctions = new ReduceFunc_t[reduceCount]; 461 if (reduceFunctions == nullptr) { 462 goto error; 463 } 464 465 for (size_t i = 0; i < reduceCount; ++i) { 466 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 467 goto error; 468 } 469 char *c = strrchr(line, '\n'); 470 if (c) { 471 *c = '\0'; 472 } 473 474 // Lookup the expanded reduce kernel. 475 strncat(line, ".expand", MAXLINE-1-strlen(line)); 476 477 reduceFunctions[i] = 478 reinterpret_cast<ReduceFunc_t>(dlsym(sharedObj, line)); 479 if (reduceFunctions[i] == nullptr) { 480 ALOGE("Failed to get function address for %s(): %s", 481 line, dlerror()); 482 goto error; 483 } 484 } 485 486 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 487 goto error; 488 } 489 if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) { 490 ALOGE("Invalid object slot count!: %s", line); 491 goto error; 492 } 493 494 for (size_t i = 0; i < objectSlotCount; ++i) { 495 uint32_t varNum = 0; 496 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 497 goto error; 498 } 499 if (sscanf(line, "%u", &varNum) != 1) { 500 ALOGE("Invalid object slot!: %s", line); 501 goto error; 502 } 503 504 if (varNum < varCount) { 505 fieldIsObject[varNum] = true; 506 } 507 } 508 509#ifndef RS_COMPATIBILITY_LIB 510 // Do not attempt to read pragmas or isThreadable flag in compat lib path. 511 // Neither is applicable for compat lib 512 513 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 514 goto error; 515 } 516 517 if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) { 518 ALOGE("Invalid pragma count!: %s", line); 519 goto error; 520 } 521 522 pragmaKeys = new const char*[pragmaCount]; 523 if (pragmaKeys == nullptr) { 524 goto error; 525 } 526 527 pragmaValues = new const char*[pragmaCount]; 528 if (pragmaValues == nullptr) { 529 goto error; 530 } 531 532 bzero(pragmaKeys, sizeof(char*) * pragmaCount); 533 bzero(pragmaValues, sizeof(char*) * pragmaCount); 534 535 for (size_t i = 0; i < pragmaCount; ++i) { 536 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 537 ALOGE("Unable to read pragma at index %zu!", i); 538 goto error; 539 } 540 char key[MAXLINE]; 541 char value[MAXLINE] = ""; // initialize in case value is empty 542 543 // pragmas can just have a key and no value. Only check to make sure 544 // that the key is not empty 545 if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s", 546 key, value) == 0 || 547 strlen(key) == 0) 548 { 549 ALOGE("Invalid pragma value!: %s", line); 550 551 goto error; 552 } 553 554 char *pKey = new char[strlen(key)+1]; 555 strcpy(pKey, key); 556 pragmaKeys[i] = pKey; 557 558 char *pValue = new char[strlen(value)+1]; 559 strcpy(pValue, value); 560 pragmaValues[i] = pValue; 561 //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue); 562 } 563 564 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 565 goto error; 566 } 567 568 char tmpFlag[4]; 569 if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) { 570 ALOGE("Invalid threadable flag!: %s", line); 571 goto error; 572 } 573 if (strcmp(tmpFlag, "yes") == 0) { 574 isThreadable = true; 575 } else if (strcmp(tmpFlag, "no") == 0) { 576 isThreadable = false; 577 } else { 578 ALOGE("Invalid threadable flag!: %s", tmpFlag); 579 goto error; 580 } 581 582 if (strgets(line, MAXLINE, &rsInfo) != nullptr) { 583 if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) { 584 ALOGE("Invalid checksum flag!: %s", line); 585 goto error; 586 } 587 } else { 588 ALOGE("Missing checksum in shared obj file"); 589 goto error; 590 } 591 592 if (expectedChecksum != 0 && checksum != expectedChecksum) { 593 ALOGE("Found invalid checksum. Expected %08x, got %08x\n", 594 expectedChecksum, checksum); 595 goto error; 596 } 597 598#endif // RS_COMPATIBILITY_LIB 599 600 // Read in information about mutable global variables provided by bcc's 601 // RSGlobalInfoPass 602 if (rsGlobalEntries) { 603 numEntries = *rsGlobalEntries; 604 if (numEntries > 0) { 605 rsAssert(rsGlobalNames); 606 rsAssert(rsGlobalAddresses); 607 rsAssert(rsGlobalSizes); 608 rsAssert(rsGlobalProperties); 609 } 610 } else { 611 ALOGD("Missing .rs.global_entries from shared object"); 612 } 613 614 return new ScriptExecutable( 615 RSContext, fieldAddress, fieldIsObject, fieldName, varCount, 616 invokeFunctions, funcCount, 617 forEachFunctions, forEachSignatures, forEachCount, 618 reduceFunctions, reduceCount, 619 pragmaKeys, pragmaValues, pragmaCount, 620 rsGlobalNames, rsGlobalAddresses, rsGlobalSizes, rsGlobalProperties, 621 numEntries, isThreadable, checksum); 622 623error: 624 625#ifndef RS_COMPATIBILITY_LIB 626 627 for (size_t idx = 0; idx < pragmaCount; ++idx) { 628 delete [] pragmaKeys[idx]; 629 delete [] pragmaValues[idx]; 630 } 631 632 delete[] pragmaValues; 633 delete[] pragmaKeys; 634#endif // RS_COMPATIBILITY_LIB 635 636 delete[] reduceFunctions; 637 638 delete[] forEachSignatures; 639 delete[] forEachFunctions; 640 641 delete[] invokeFunctions; 642 643 for (size_t i = 0; i < varCount; i++) { 644 delete[] fieldName[i]; 645 } 646 delete[] fieldName; 647 delete[] fieldIsObject; 648 delete[] fieldAddress; 649 650 return nullptr; 651} 652 653void* ScriptExecutable::getFieldAddress(const char* name) const { 654 // TODO: improve this by using a hash map. 655 for (size_t i = 0; i < mExportedVarCount; i++) { 656 if (strcmp(name, mFieldName[i]) == 0) { 657 return mFieldAddress[i]; 658 } 659 } 660 return nullptr; 661} 662 663bool ScriptExecutable::dumpGlobalInfo() const { 664 ALOGE("Globals: %p %p %p", mGlobalAddresses, mGlobalSizes, mGlobalNames); 665 ALOGE("P - Pointer"); 666 ALOGE(" C - Constant"); 667 ALOGE(" S - Static"); 668 for (int i = 0; i < mGlobalEntries; i++) { 669 ALOGE("Global[%d]: %p %zu %s", i, mGlobalAddresses[i], mGlobalSizes[i], 670 mGlobalNames[i]); 671 uint32_t properties = mGlobalProperties[i]; 672 ALOGE("%c%c%c Type: %u", 673 isGlobalPointer(properties) ? 'P' : ' ', 674 isGlobalConstant(properties) ? 'C' : ' ', 675 isGlobalStatic(properties) ? 'S' : ' ', 676 getGlobalRsType(properties)); 677 } 678 return true; 679} 680 681} // namespace renderscript 682} // namespace android 683