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