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