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