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