rsCpuExecutable.cpp revision 682672e36b05349bc4d9dee74e9fab73ce804183
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
316    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
317        return nullptr;
318    }
319    if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
320        ALOGE("Invalid export var count!: %s", line);
321        return nullptr;
322    }
323
324    fieldAddress = new void*[varCount];
325    if (fieldAddress == nullptr) {
326        return nullptr;
327    }
328
329    fieldIsObject = new bool[varCount];
330    if (fieldIsObject == nullptr) {
331        goto error;
332    }
333
334    fieldName = new char*[varCount];
335    if (fieldName == nullptr) {
336        goto error;
337    }
338
339    for (size_t i = 0; i < varCount; ++i) {
340        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
341            goto error;
342        }
343        char *c = strrchr(line, '\n');
344        if (c) {
345            *c = '\0';
346        }
347        void* addr = dlsym(sharedObj, line);
348        if (addr == nullptr) {
349            ALOGE("Failed to find variable address for %s: %s",
350                  line, dlerror());
351            // Not a critical error if we don't find a global variable.
352        }
353        fieldAddress[i] = addr;
354        fieldIsObject[i] = false;
355        fieldName[i] = new char[strlen(line)+1];
356        strcpy(fieldName[i], line);
357    }
358
359    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
360        goto error;
361    }
362    if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
363        ALOGE("Invalid export func count!: %s", line);
364        goto error;
365    }
366
367    invokeFunctions = new InvokeFunc_t[funcCount];
368    if (invokeFunctions == nullptr) {
369        goto error;
370    }
371
372    for (size_t i = 0; i < funcCount; ++i) {
373        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
374            goto error;
375        }
376        char *c = strrchr(line, '\n');
377        if (c) {
378            *c = '\0';
379        }
380
381        invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
382        if (invokeFunctions[i] == nullptr) {
383            ALOGE("Failed to get function address for %s(): %s",
384                  line, dlerror());
385            goto error;
386        }
387    }
388
389    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
390        goto error;
391    }
392    if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
393        ALOGE("Invalid export forEach count!: %s", line);
394        goto error;
395    }
396
397    forEachFunctions = new ForEachFunc_t[forEachCount];
398    if (forEachFunctions == nullptr) {
399        goto error;
400    }
401
402    forEachSignatures = new uint32_t[forEachCount];
403    if (forEachSignatures == nullptr) {
404        goto error;
405    }
406
407    for (size_t i = 0; i < forEachCount; ++i) {
408        unsigned int tmpSig = 0;
409        char tmpName[MAXLINE];
410
411        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
412            goto error;
413        }
414        if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s",
415                   &tmpSig, tmpName) != 2) {
416          ALOGE("Invalid export forEach!: %s", line);
417          goto error;
418        }
419
420        // Lookup the expanded ForEach kernel.
421        strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName));
422        forEachSignatures[i] = tmpSig;
423        forEachFunctions[i] =
424            (ForEachFunc_t) dlsym(sharedObj, tmpName);
425        if (i != 0 && forEachFunctions[i] == nullptr &&
426            strcmp(tmpName, "root.expand")) {
427            // Ignore missing root.expand functions.
428            // root() is always specified at location 0.
429            ALOGE("Failed to find forEach function address for %s: %s",
430                  tmpName, dlerror());
431            goto error;
432        }
433    }
434
435    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
436        goto error;
437    }
438    if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
439        ALOGE("Invalid object slot count!: %s", line);
440        goto error;
441    }
442
443    for (size_t i = 0; i < objectSlotCount; ++i) {
444        uint32_t varNum = 0;
445        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
446            goto error;
447        }
448        if (sscanf(line, "%u", &varNum) != 1) {
449            ALOGE("Invalid object slot!: %s", line);
450            goto error;
451        }
452
453        if (varNum < varCount) {
454            fieldIsObject[varNum] = true;
455        }
456    }
457
458#ifndef RS_COMPATIBILITY_LIB
459    // Do not attempt to read pragmas or isThreadable flag in compat lib path.
460    // Neither is applicable for compat lib
461
462    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
463        goto error;
464    }
465
466    if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
467        ALOGE("Invalid pragma count!: %s", line);
468        goto error;
469    }
470
471    pragmaKeys = new const char*[pragmaCount];
472    if (pragmaKeys == nullptr) {
473        goto error;
474    }
475
476    pragmaValues = new const char*[pragmaCount];
477    if (pragmaValues == nullptr) {
478        goto error;
479    }
480
481    bzero(pragmaKeys, sizeof(char*) * pragmaCount);
482    bzero(pragmaValues, sizeof(char*) * pragmaCount);
483
484    for (size_t i = 0; i < pragmaCount; ++i) {
485        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
486            ALOGE("Unable to read pragma at index %zu!", i);
487            goto error;
488        }
489        char key[MAXLINE];
490        char value[MAXLINE] = ""; // initialize in case value is empty
491
492        // pragmas can just have a key and no value.  Only check to make sure
493        // that the key is not empty
494        if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s",
495                   key, value) == 0 ||
496            strlen(key) == 0)
497        {
498            ALOGE("Invalid pragma value!: %s", line);
499
500            goto error;
501        }
502
503        char *pKey = new char[strlen(key)+1];
504        strcpy(pKey, key);
505        pragmaKeys[i] = pKey;
506
507        char *pValue = new char[strlen(value)+1];
508        strcpy(pValue, value);
509        pragmaValues[i] = pValue;
510        //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
511    }
512
513    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
514        goto error;
515    }
516
517    char tmpFlag[4];
518    if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) {
519        ALOGE("Invalid threadable flag!: %s", line);
520        goto error;
521    }
522    if (strcmp(tmpFlag, "yes") == 0) {
523        isThreadable = true;
524    } else if (strcmp(tmpFlag, "no") == 0) {
525        isThreadable = false;
526    } else {
527        ALOGE("Invalid threadable flag!: %s", tmpFlag);
528        goto error;
529    }
530
531    if (strgets(line, MAXLINE, &rsInfo) != nullptr) {
532        if (sscanf(line, CHECKSUM_STR "%08x", &checksum) != 1) {
533            ALOGE("Invalid checksum flag!: %s", line);
534            goto error;
535        }
536    } else {
537        ALOGE("Missing checksum in shared obj file");
538        goto error;
539    }
540
541    if (expectedChecksum != 0 && checksum != expectedChecksum) {
542        ALOGE("Found invalid checksum.  Expected %08x, got %08x\n",
543              expectedChecksum, checksum);
544        goto error;
545    }
546
547#endif  // RS_COMPATIBILITY_LIB
548
549    return new ScriptExecutable(
550        RSContext, fieldAddress, fieldIsObject, fieldName, varCount,
551        invokeFunctions, funcCount,
552        forEachFunctions, forEachSignatures, forEachCount,
553        pragmaKeys, pragmaValues, pragmaCount,
554        isThreadable, checksum);
555
556error:
557
558#ifndef RS_COMPATIBILITY_LIB
559
560    for (size_t idx = 0; idx < pragmaCount; ++idx) {
561        delete [] pragmaKeys[idx];
562        delete [] pragmaValues[idx];
563    }
564
565    delete[] pragmaValues;
566    delete[] pragmaKeys;
567#endif  // RS_COMPATIBILITY_LIB
568
569    delete[] forEachSignatures;
570    delete[] forEachFunctions;
571
572    delete[] invokeFunctions;
573
574    for (size_t i = 0; i < varCount; i++) {
575        delete[] fieldName[i];
576    }
577    delete[] fieldName;
578    delete[] fieldIsObject;
579    delete[] fieldAddress;
580
581    return nullptr;
582}
583
584void* ScriptExecutable::getFieldAddress(const char* name) const {
585    // TODO: improve this by using a hash map.
586    for (size_t i = 0; i < mExportedVarCount; i++) {
587        if (strcmp(name, mFieldName[i]) == 0) {
588            return mFieldAddress[i];
589        }
590    }
591    return nullptr;
592}
593
594}  // namespace renderscript
595}  // namespace android
596