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