rsCpuExecutable.cpp revision 0be70c6dd8897603530dbc7411f27d9dbd4fee9a
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 *driverName,
114                                             const char *cacheDir,
115                                             const char *resName) {
116    std::string sharedLibName = findSharedObjectName(cacheDir, resName);
117    std::string objFileName = cacheDir;
118    objFileName.append("/");
119    objFileName.append(resName);
120    objFileName.append(".o");
121    // Should be something like "libRSDriver.so".
122    std::string linkDriverName = driverName;
123    // Remove ".so" and replace "lib" with "-l".
124    // This will leave us with "-lRSDriver" instead.
125    linkDriverName.erase(linkDriverName.length() - 3);
126    linkDriverName.replace(0, 3, "-l");
127
128    const char *compiler_rt = SYSLIBPATH"/libcompiler_rt.so";
129    const char *mTriple = "-mtriple=" DEFAULT_TARGET_TRIPLE_STRING;
130    const char *libPath = "--library-path=" SYSLIBPATH;
131    const char *vendorLibPath = "--library-path=" SYSLIBPATH_VENDOR;
132
133    std::vector<const char *> args = {
134        LD_EXE_PATH,
135        "-shared",
136        "-nostdlib",
137        compiler_rt, mTriple, vendorLibPath, libPath,
138        linkDriverName.c_str(), "-lm", "-lc",
139        objFileName.c_str(),
140        "-o", sharedLibName.c_str(),
141        nullptr
142    };
143
144    return rsuExecuteCommand(LD_EXE_PATH, args.size()-1, args.data());
145
146}
147
148#endif  // RS_COMPATIBILITY_LIB
149
150const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
151
152void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir,
153                                            const char *resName,
154                                            const char *nativeLibDir) {
155    void *loaded = nullptr;
156
157#if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
158    std::string scriptSOName = findSharedObjectName(nativeLibDir, resName);
159#else
160    std::string scriptSOName = findSharedObjectName(cacheDir, resName);
161#endif
162
163    // We should check if we can load the library from the standard app
164    // location for shared libraries first.
165    loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName);
166
167    if (loaded == nullptr) {
168        ALOGE("Unable to open shared library (%s): %s",
169              scriptSOName.c_str(), dlerror());
170
171#ifdef RS_COMPATIBILITY_LIB
172        // One final attempt to find the library in "/system/lib".
173        // We do this to allow bundled applications to use the compatibility
174        // library fallback path. Those applications don't have a private
175        // library path, so they need to install to the system directly.
176        // Note that this is really just a testing path.
177        std::string scriptSONameSystem("/system/lib/librs.");
178        scriptSONameSystem.append(resName);
179        scriptSONameSystem.append(".so");
180        loaded = loadSOHelper(scriptSONameSystem.c_str(), cacheDir,
181                              resName);
182        if (loaded == nullptr) {
183            ALOGE("Unable to open system shared library (%s): %s",
184                  scriptSONameSystem.c_str(), dlerror());
185        }
186#endif
187    }
188
189    return loaded;
190}
191
192void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
193                                       const char *resName) {
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        loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
212        if (loaded) {
213            LoadedLibraries.insert(origName);
214        }
215        return loaded;
216    }
217
218    std::string newName(cacheDir);
219
220    // Append RS_CACHE_DIR only if it is not found in cacheDir
221    // In driver mode, RS_CACHE_DIR is already appended to cacheDir.
222    if (newName.find(RS_CACHE_DIR) == std::string::npos) {
223        newName.append("/");
224        newName.append(RS_CACHE_DIR);
225        newName.append("/");
226    }
227
228    if (!ensureCacheDirExists(newName.c_str())) {
229        ALOGE("Could not verify or create cache dir: %s", cacheDir);
230        return nullptr;
231    }
232
233    // Construct an appropriately randomized filename for the copy.
234    newName.append("librs.");
235    newName.append(resName);
236    newName.append("#");
237    newName.append(getRandomString(6));  // 62^6 potential filename variants.
238    newName.append(".so");
239
240    int r = copyFile(newName.c_str(), origName);
241    if (r != 0) {
242        ALOGE("Could not create copy %s -> %s", origName, newName.c_str());
243        return nullptr;
244    }
245    loaded = dlopen(newName.c_str(), RTLD_NOW | RTLD_LOCAL);
246    r = unlink(newName.c_str());
247    if (r != 0) {
248        ALOGE("Could not unlink copy %s", newName.c_str());
249    }
250    if (loaded) {
251        LoadedLibraries.insert(newName.c_str());
252    }
253
254    return loaded;
255}
256
257#define MAXLINE 500
258#define MAKE_STR_HELPER(S) #S
259#define MAKE_STR(S) MAKE_STR_HELPER(S)
260#define EXPORT_VAR_STR "exportVarCount: "
261#define EXPORT_FUNC_STR "exportFuncCount: "
262#define EXPORT_FOREACH_STR "exportForEachCount: "
263#define OBJECT_SLOT_STR "objectSlotCount: "
264#define PRAGMA_STR "pragmaCount: "
265#define THREADABLE_STR "isThreadable: "
266#define CHECKSUM_STR "buildChecksum: "
267
268// Copy up to a newline or size chars from str -> s, updating str
269// Returns s when successful and nullptr when '\0' is finally reached.
270static char* strgets(char *s, int size, const char **ppstr) {
271    if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) {
272        return nullptr;
273    }
274
275    int i;
276    for (i = 0; i < (size - 1); i++) {
277        s[i] = **ppstr;
278        (*ppstr)++;
279        if (s[i] == '\0') {
280            return s;
281        } else if (s[i] == '\n') {
282            s[i+1] = '\0';
283            return s;
284        }
285    }
286
287    // size has been exceeded.
288    s[i] = '\0';
289
290    return s;
291}
292
293ScriptExecutable* ScriptExecutable::createFromSharedObject(
294    Context* RSContext, void* sharedObj, uint32_t expectedChecksum) {
295    char line[MAXLINE];
296
297    size_t varCount = 0;
298    size_t funcCount = 0;
299    size_t forEachCount = 0;
300    size_t objectSlotCount = 0;
301    size_t pragmaCount = 0;
302    bool isThreadable = true;
303
304    void** fieldAddress = nullptr;
305    bool* fieldIsObject = nullptr;
306    char** fieldName = nullptr;
307    InvokeFunc_t* invokeFunctions = nullptr;
308    ForEachFunc_t* forEachFunctions = nullptr;
309    uint32_t* forEachSignatures = nullptr;
310    const char ** pragmaKeys = nullptr;
311    const char ** pragmaValues = nullptr;
312    uint32_t checksum = 0;
313
314    const char *rsInfo = (const char *) dlsym(sharedObj, ".rs.info");
315    int numEntries = 0;
316    const int *rsGlobalEntries = (const int *) dlsym(sharedObj, ".rs.global_entries");
317    const char **rsGlobalNames = (const char **) dlsym(sharedObj, ".rs.global_names");
318    const void **rsGlobalAddresses = (const void **) dlsym(sharedObj, ".rs.global_addresses");
319    const size_t *rsGlobalSizes = (const size_t *) dlsym(sharedObj, ".rs.global_sizes");
320
321    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
322        return nullptr;
323    }
324    if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
325        ALOGE("Invalid export var count!: %s", line);
326        return nullptr;
327    }
328
329    fieldAddress = new void*[varCount];
330    if (fieldAddress == nullptr) {
331        return nullptr;
332    }
333
334    fieldIsObject = new bool[varCount];
335    if (fieldIsObject == nullptr) {
336        goto error;
337    }
338
339    fieldName = new char*[varCount];
340    if (fieldName == nullptr) {
341        goto error;
342    }
343
344    for (size_t i = 0; i < varCount; ++i) {
345        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
346            goto error;
347        }
348        char *c = strrchr(line, '\n');
349        if (c) {
350            *c = '\0';
351        }
352        void* addr = dlsym(sharedObj, line);
353        if (addr == nullptr) {
354            ALOGE("Failed to find variable address for %s: %s",
355                  line, dlerror());
356            // Not a critical error if we don't find a global variable.
357        }
358        fieldAddress[i] = addr;
359        fieldIsObject[i] = false;
360        fieldName[i] = new char[strlen(line)+1];
361        strcpy(fieldName[i], line);
362    }
363
364    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
365        goto error;
366    }
367    if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
368        ALOGE("Invalid export func count!: %s", line);
369        goto error;
370    }
371
372    invokeFunctions = new InvokeFunc_t[funcCount];
373    if (invokeFunctions == nullptr) {
374        goto error;
375    }
376
377    for (size_t i = 0; i < funcCount; ++i) {
378        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
379            goto error;
380        }
381        char *c = strrchr(line, '\n');
382        if (c) {
383            *c = '\0';
384        }
385
386        invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
387        if (invokeFunctions[i] == nullptr) {
388            ALOGE("Failed to get function address for %s(): %s",
389                  line, dlerror());
390            goto error;
391        }
392    }
393
394    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
395        goto error;
396    }
397    if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
398        ALOGE("Invalid export forEach count!: %s", line);
399        goto error;
400    }
401
402    forEachFunctions = new ForEachFunc_t[forEachCount];
403    if (forEachFunctions == nullptr) {
404        goto error;
405    }
406
407    forEachSignatures = new uint32_t[forEachCount];
408    if (forEachSignatures == nullptr) {
409        goto error;
410    }
411
412    for (size_t i = 0; i < forEachCount; ++i) {
413        unsigned int tmpSig = 0;
414        char tmpName[MAXLINE];
415
416        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
417            goto error;
418        }
419        if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s",
420                   &tmpSig, tmpName) != 2) {
421          ALOGE("Invalid export forEach!: %s", line);
422          goto error;
423        }
424
425        // Lookup the expanded ForEach kernel.
426        strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName));
427        forEachSignatures[i] = tmpSig;
428        forEachFunctions[i] =
429            (ForEachFunc_t) dlsym(sharedObj, tmpName);
430        if (i != 0 && forEachFunctions[i] == nullptr &&
431            strcmp(tmpName, "root.expand")) {
432            // Ignore missing root.expand functions.
433            // root() is always specified at location 0.
434            ALOGE("Failed to find forEach function address for %s: %s",
435                  tmpName, dlerror());
436            goto error;
437        }
438    }
439
440    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
441        goto error;
442    }
443    if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
444        ALOGE("Invalid object slot count!: %s", line);
445        goto error;
446    }
447
448    for (size_t i = 0; i < objectSlotCount; ++i) {
449        uint32_t varNum = 0;
450        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
451            goto error;
452        }
453        if (sscanf(line, "%u", &varNum) != 1) {
454            ALOGE("Invalid object slot!: %s", line);
455            goto error;
456        }
457
458        if (varNum < varCount) {
459            fieldIsObject[varNum] = true;
460        }
461    }
462
463#ifndef RS_COMPATIBILITY_LIB
464    // Do not attempt to read pragmas or isThreadable flag in compat lib path.
465    // Neither is applicable for compat lib
466
467    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
468        goto error;
469    }
470
471    if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
472        ALOGE("Invalid pragma count!: %s", line);
473        goto error;
474    }
475
476    pragmaKeys = new const char*[pragmaCount];
477    if (pragmaKeys == nullptr) {
478        goto error;
479    }
480
481    pragmaValues = new const char*[pragmaCount];
482    if (pragmaValues == nullptr) {
483        goto error;
484    }
485
486    bzero(pragmaKeys, sizeof(char*) * pragmaCount);
487    bzero(pragmaValues, sizeof(char*) * pragmaCount);
488
489    for (size_t i = 0; i < pragmaCount; ++i) {
490        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
491            ALOGE("Unable to read pragma at index %zu!", i);
492            goto error;
493        }
494        char key[MAXLINE];
495        char value[MAXLINE] = ""; // initialize in case value is empty
496
497        // pragmas can just have a key and no value.  Only check to make sure
498        // that the key is not empty
499        if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s",
500                   key, value) == 0 ||
501            strlen(key) == 0)
502        {
503            ALOGE("Invalid pragma value!: %s", line);
504
505            goto error;
506        }
507
508        char *pKey = new char[strlen(key)+1];
509        strcpy(pKey, key);
510        pragmaKeys[i] = pKey;
511
512        char *pValue = new char[strlen(value)+1];
513        strcpy(pValue, value);
514        pragmaValues[i] = pValue;
515        //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
516    }
517
518    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
519        goto error;
520    }
521
522    char tmpFlag[4];
523    if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) {
524        ALOGE("Invalid threadable flag!: %s", line);
525        goto error;
526    }
527    if (strcmp(tmpFlag, "yes") == 0) {
528        isThreadable = true;
529    } else if (strcmp(tmpFlag, "no") == 0) {
530        isThreadable = false;
531    } else {
532        ALOGE("Invalid threadable flag!: %s", tmpFlag);
533        goto error;
534    }
535
536    if (strgets(line, MAXLINE, &rsInfo) != nullptr) {
537        if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) {
538            ALOGE("Invalid checksum flag!: %s", line);
539            goto error;
540        }
541    } else {
542        ALOGE("Missing checksum in shared obj file");
543        goto error;
544    }
545
546    if (expectedChecksum != 0 && checksum != expectedChecksum) {
547        ALOGE("Found invalid checksum.  Expected %08x, got %08x\n",
548              expectedChecksum, checksum);
549        goto error;
550    }
551
552#endif  // RS_COMPATIBILITY_LIB
553
554    // Read in information about mutable global variables provided by bcc's
555    // RSGlobalInfoPass
556    if (rsGlobalEntries) {
557        numEntries = *rsGlobalEntries;
558        if (numEntries > 0) {
559            rsAssert(rsGlobalNames);
560            rsAssert(rsGlobalAddresses);
561            rsAssert(rsGlobalSizes);
562        }
563    } else {
564        ALOGD("Missing .rs.global_entries from shared object");
565    }
566
567    return new ScriptExecutable(
568        RSContext, fieldAddress, fieldIsObject, fieldName, varCount,
569        invokeFunctions, funcCount,
570        forEachFunctions, forEachSignatures, forEachCount,
571        pragmaKeys, pragmaValues, pragmaCount,
572        rsGlobalNames, rsGlobalAddresses, rsGlobalSizes, numEntries,
573        isThreadable, checksum);
574
575error:
576
577#ifndef RS_COMPATIBILITY_LIB
578
579    for (size_t idx = 0; idx < pragmaCount; ++idx) {
580        delete [] pragmaKeys[idx];
581        delete [] pragmaValues[idx];
582    }
583
584    delete[] pragmaValues;
585    delete[] pragmaKeys;
586#endif  // RS_COMPATIBILITY_LIB
587
588    delete[] forEachSignatures;
589    delete[] forEachFunctions;
590
591    delete[] invokeFunctions;
592
593    for (size_t i = 0; i < varCount; i++) {
594        delete[] fieldName[i];
595    }
596    delete[] fieldName;
597    delete[] fieldIsObject;
598    delete[] fieldAddress;
599
600    return nullptr;
601}
602
603void* ScriptExecutable::getFieldAddress(const char* name) const {
604    // TODO: improve this by using a hash map.
605    for (size_t i = 0; i < mExportedVarCount; i++) {
606        if (strcmp(name, mFieldName[i]) == 0) {
607            return mFieldAddress[i];
608        }
609    }
610    return nullptr;
611}
612
613bool ScriptExecutable::dumpGlobalInfo() const {
614    ALOGE("Globals: %p %p %p", mGlobalAddresses, mGlobalSizes, mGlobalNames);
615    for (int i = 0; i < mGlobalEntries; i++) {
616        ALOGE("Global[%d]: %p %zu %s", i, mGlobalAddresses[i], mGlobalSizes[i],
617              mGlobalNames[i]);
618    }
619    return true;
620}
621
622}  // namespace renderscript
623}  // namespace android
624