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