rsCpuExecutable.cpp revision 38014b1ee631ee61e457b850138d3ea6eb73cb1b
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 307 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 308 return nullptr; 309 } 310 if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) { 311 ALOGE("Invalid export var count!: %s", line); 312 return nullptr; 313 } 314 315 fieldAddress = new void*[varCount]; 316 if (fieldAddress == nullptr) { 317 return nullptr; 318 } 319 320 fieldIsObject = new bool[varCount]; 321 if (fieldIsObject == nullptr) { 322 goto error; 323 } 324 325 fieldName = new char*[varCount]; 326 if (fieldName == nullptr) { 327 goto error; 328 } 329 330 for (size_t i = 0; i < varCount; ++i) { 331 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 332 goto error; 333 } 334 char *c = strrchr(line, '\n'); 335 if (c) { 336 *c = '\0'; 337 } 338 void* addr = dlsym(sharedObj, line); 339 if (addr == nullptr) { 340 ALOGE("Failed to find variable address for %s: %s", 341 line, dlerror()); 342 // Not a critical error if we don't find a global variable. 343 } 344 fieldAddress[i] = addr; 345 fieldIsObject[i] = false; 346 fieldName[i] = new char[strlen(line)+1]; 347 strcpy(fieldName[i], line); 348 } 349 350 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 351 goto error; 352 } 353 if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) { 354 ALOGE("Invalid export func count!: %s", line); 355 goto error; 356 } 357 358 invokeFunctions = new InvokeFunc_t[funcCount]; 359 if (invokeFunctions == nullptr) { 360 goto error; 361 } 362 363 for (size_t i = 0; i < funcCount; ++i) { 364 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 365 goto error; 366 } 367 char *c = strrchr(line, '\n'); 368 if (c) { 369 *c = '\0'; 370 } 371 372 invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line); 373 if (invokeFunctions[i] == nullptr) { 374 ALOGE("Failed to get function address for %s(): %s", 375 line, dlerror()); 376 goto error; 377 } 378 } 379 380 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 381 goto error; 382 } 383 if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) { 384 ALOGE("Invalid export forEach count!: %s", line); 385 goto error; 386 } 387 388 forEachFunctions = new ForEachFunc_t[forEachCount]; 389 if (forEachFunctions == nullptr) { 390 goto error; 391 } 392 393 forEachSignatures = new uint32_t[forEachCount]; 394 if (forEachSignatures == nullptr) { 395 goto error; 396 } 397 398 for (size_t i = 0; i < forEachCount; ++i) { 399 unsigned int tmpSig = 0; 400 char tmpName[MAXLINE]; 401 402 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 403 goto error; 404 } 405 if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s", 406 &tmpSig, tmpName) != 2) { 407 ALOGE("Invalid export forEach!: %s", line); 408 goto error; 409 } 410 411 // Lookup the expanded ForEach kernel. 412 strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName)); 413 forEachSignatures[i] = tmpSig; 414 forEachFunctions[i] = 415 (ForEachFunc_t) dlsym(sharedObj, tmpName); 416 if (i != 0 && forEachFunctions[i] == nullptr && 417 strcmp(tmpName, "root.expand")) { 418 // Ignore missing root.expand functions. 419 // root() is always specified at location 0. 420 ALOGE("Failed to find forEach function address for %s: %s", 421 tmpName, dlerror()); 422 goto error; 423 } 424 } 425 426 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 427 goto error; 428 } 429 if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) { 430 ALOGE("Invalid object slot count!: %s", line); 431 goto error; 432 } 433 434 for (size_t i = 0; i < objectSlotCount; ++i) { 435 uint32_t varNum = 0; 436 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 437 goto error; 438 } 439 if (sscanf(line, "%u", &varNum) != 1) { 440 ALOGE("Invalid object slot!: %s", line); 441 goto error; 442 } 443 444 if (varNum < varCount) { 445 fieldIsObject[varNum] = true; 446 } 447 } 448 449#ifndef RS_COMPATIBILITY_LIB 450 // Do not attempt to read pragmas or isThreadable flag in compat lib path. 451 // Neither is applicable for compat lib 452 453 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 454 goto error; 455 } 456 457 if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) { 458 ALOGE("Invalid pragma count!: %s", line); 459 goto error; 460 } 461 462 pragmaKeys = new const char*[pragmaCount]; 463 if (pragmaKeys == nullptr) { 464 goto error; 465 } 466 467 pragmaValues = new const char*[pragmaCount]; 468 if (pragmaValues == nullptr) { 469 goto error; 470 } 471 472 bzero(pragmaKeys, sizeof(char*) * pragmaCount); 473 bzero(pragmaValues, sizeof(char*) * pragmaCount); 474 475 for (size_t i = 0; i < pragmaCount; ++i) { 476 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 477 ALOGE("Unable to read pragma at index %zu!", i); 478 goto error; 479 } 480 char key[MAXLINE]; 481 char value[MAXLINE] = ""; // initialize in case value is empty 482 483 // pragmas can just have a key and no value. Only check to make sure 484 // that the key is not empty 485 if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s", 486 key, value) == 0 || 487 strlen(key) == 0) 488 { 489 ALOGE("Invalid pragma value!: %s", line); 490 491 goto error; 492 } 493 494 char *pKey = new char[strlen(key)+1]; 495 strcpy(pKey, key); 496 pragmaKeys[i] = pKey; 497 498 char *pValue = new char[strlen(value)+1]; 499 strcpy(pValue, value); 500 pragmaValues[i] = pValue; 501 //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue); 502 } 503 504 if (strgets(line, MAXLINE, &rsInfo) == nullptr) { 505 goto error; 506 } 507 508 char tmpFlag[4]; 509 if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) { 510 ALOGE("Invalid threadable flag!: %s", line); 511 goto error; 512 } 513 if (strcmp(tmpFlag, "yes") == 0) { 514 isThreadable = true; 515 } else if (strcmp(tmpFlag, "no") == 0) { 516 isThreadable = false; 517 } else { 518 ALOGE("Invalid threadable flag!: %s", tmpFlag); 519 goto error; 520 } 521 522 if (strgets(line, MAXLINE, &rsInfo) != nullptr) { 523 if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) { 524 ALOGE("Invalid checksum flag!: %s", line); 525 goto error; 526 } 527 } else { 528 ALOGE("Missing checksum in shared obj file"); 529 goto error; 530 } 531 532 if (expectedChecksum != 0 && checksum != expectedChecksum) { 533 ALOGE("Found invalid checksum. Expected %08x, got %08x\n", 534 expectedChecksum, checksum); 535 goto error; 536 } 537 538#endif // RS_COMPATIBILITY_LIB 539 540 return new ScriptExecutable( 541 RSContext, fieldAddress, fieldIsObject, fieldName, varCount, 542 invokeFunctions, funcCount, 543 forEachFunctions, forEachSignatures, forEachCount, 544 pragmaKeys, pragmaValues, pragmaCount, 545 isThreadable, checksum); 546 547error: 548 549#ifndef RS_COMPATIBILITY_LIB 550 551 for (size_t idx = 0; idx < pragmaCount; ++idx) { 552 delete [] pragmaKeys[idx]; 553 delete [] pragmaValues[idx]; 554 } 555 556 delete[] pragmaValues; 557 delete[] pragmaKeys; 558#endif // RS_COMPATIBILITY_LIB 559 560 delete[] forEachSignatures; 561 delete[] forEachFunctions; 562 563 delete[] invokeFunctions; 564 565 for (size_t i = 0; i < varCount; i++) { 566 delete[] fieldName[i]; 567 } 568 delete[] fieldName; 569 delete[] fieldIsObject; 570 delete[] fieldAddress; 571 572 return nullptr; 573} 574 575void* ScriptExecutable::getFieldAddress(const char* name) const { 576 // TODO: improve this by using a hash map. 577 for (size_t i = 0; i < mExportedVarCount; i++) { 578 if (strcmp(name, mFieldName[i]) == 0) { 579 return mFieldAddress[i]; 580 } 581 } 582 return nullptr; 583} 584 585} // namespace renderscript 586} // namespace android 587