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