Moved class ScriptExecutable to its own module

b/19283946

This move also included the related class SharedObjectUtils.

Change-Id: Iefd587b659c4ad99340c330955b41cd92df45563
diff --git a/cpu_ref/Android.mk b/cpu_ref/Android.mk
index 7c63c95..a44db9c 100644
--- a/cpu_ref/Android.mk
+++ b/cpu_ref/Android.mk
@@ -20,6 +20,7 @@
 
 LOCAL_SRC_FILES:= \
 	rsCpuCore.cpp \
+	rsCpuExecutable.cpp \
 	rsCpuScript.cpp \
 	rsCpuRuntimeMath.cpp \
 	rsCpuRuntimeMathFuncs.cpp \
diff --git a/cpu_ref/rsCpuExecutable.cpp b/cpu_ref/rsCpuExecutable.cpp
new file mode 100644
index 0000000..ae75b07
--- /dev/null
+++ b/cpu_ref/rsCpuExecutable.cpp
@@ -0,0 +1,578 @@
+#include "rsCpuExecutable.h"
+#include "rsCppUtils.h"
+
+#include <fstream>
+#include <set>
+#include <memory>
+
+#ifdef RS_COMPATIBILITY_LIB
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#else
+#include "bcc/Config/Config.h"
+#include <bcc/Renderscript/RSInfo.h>
+#include <sys/wait.h>
+#endif
+
+#include <dlfcn.h>
+
+namespace android {
+namespace renderscript {
+
+namespace {
+
+// Create a len length string containing random characters from [A-Za-z0-9].
+static std::string getRandomString(size_t len) {
+    char buf[len + 1];
+    for (size_t i = 0; i < len; i++) {
+        uint32_t r = arc4random() & 0xffff;
+        r %= 62;
+        if (r < 26) {
+            // lowercase
+            buf[i] = 'a' + r;
+        } else if (r < 52) {
+            // uppercase
+            buf[i] = 'A' + (r - 26);
+        } else {
+            // Use a number
+            buf[i] = '0' + (r - 52);
+        }
+    }
+    buf[len] = '\0';
+    return std::string(buf);
+}
+
+// Check if a path exists and attempt to create it if it doesn't.
+static bool ensureCacheDirExists(const char *path) {
+    if (access(path, R_OK | W_OK | X_OK) == 0) {
+        // Done if we can rwx the directory
+        return true;
+    }
+    if (mkdir(path, 0700) == 0) {
+        return true;
+    }
+    return false;
+}
+
+// Copy the file named \p srcFile to \p dstFile.
+// Return 0 on success and -1 if anything wasn't copied.
+static int copyFile(const char *dstFile, const char *srcFile) {
+    std::ifstream srcStream(srcFile);
+    if (!srcStream) {
+        ALOGE("Could not verify or read source file: %s", srcFile);
+        return -1;
+    }
+    std::ofstream dstStream(dstFile);
+    if (!dstStream) {
+        ALOGE("Could not verify or write destination file: %s", dstFile);
+        return -1;
+    }
+    dstStream << srcStream.rdbuf();
+    if (!dstStream) {
+        ALOGE("Could not write destination file: %s", dstFile);
+        return -1;
+    }
+
+    srcStream.close();
+    dstStream.close();
+
+    return 0;
+}
+
+static std::string findSharedObjectName(const char *cacheDir,
+                                        const char *resName) {
+#ifndef RS_SERVER
+    std::string scriptSOName(cacheDir);
+#if defined(RS_COMPATIBILITY_LIB) && !defined(__LP64__)
+    size_t cutPos = scriptSOName.rfind("cache");
+    if (cutPos != std::string::npos) {
+        scriptSOName.erase(cutPos);
+    } else {
+        ALOGE("Found peculiar cacheDir (missing \"cache\"): %s", cacheDir);
+    }
+    scriptSOName.append("/lib/librs.");
+#else
+    scriptSOName.append("/librs.");
+#endif // RS_COMPATIBILITY_LIB
+
+#else
+    std::string scriptSOName("lib");
+#endif // RS_SERVER
+    scriptSOName.append(resName);
+    scriptSOName.append(".so");
+
+    return scriptSOName;
+}
+
+}  // anonymous namespace
+
+const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc";
+const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache";
+
+#ifndef RS_COMPATIBILITY_LIB
+
+bool SharedLibraryUtils::createSharedLibrary(const char *cacheDir, const char *resName) {
+    std::string sharedLibName = findSharedObjectName(cacheDir, resName);
+    std::string objFileName = cacheDir;
+    objFileName.append("/");
+    objFileName.append(resName);
+    objFileName.append(".o");
+
+    const char *compiler_rt = SYSLIBPATH"/libcompiler_rt.so";
+    std::vector<const char *> args = {
+        LD_EXE_PATH,
+        "-shared",
+        "-nostdlib",
+        compiler_rt,
+        "-mtriple", DEFAULT_TARGET_TRIPLE_STRING,
+        "-L", SYSLIBPATH,
+        "-lRSDriver", "-lm", "-lc",
+        objFileName.c_str(),
+        "-o", sharedLibName.c_str(),
+        nullptr
+    };
+
+    std::unique_ptr<const char> joined(
+        rsuJoinStrings(args.size()-1, args.data()));
+    std::string cmdLineStr (joined.get());
+
+    pid_t pid = fork();
+
+    switch (pid) {
+    case -1: {  // Error occurred (we attempt no recovery)
+        ALOGE("Couldn't fork for linker (%s) execution", LD_EXE_PATH);
+        return false;
+    }
+    case 0: {  // Child process
+        ALOGV("Invoking ld.mc with args '%s'", cmdLineStr.c_str());
+        execv(LD_EXE_PATH, (char* const*) args.data());
+
+        ALOGE("execv() failed: %s", strerror(errno));
+        abort();
+        return false;
+    }
+    default: {  // Parent process (actual driver)
+        // Wait on child process to finish compiling the source.
+        int status = 0;
+        pid_t w = waitpid(pid, &status, 0);
+        if (w == -1) {
+            ALOGE("Could not wait for linker (%s)", LD_EXE_PATH);
+            return false;
+        }
+
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+            return true;
+        }
+
+        ALOGE("Linker (%s) terminated unexpectedly", LD_EXE_PATH);
+        return false;
+    }
+    }
+}
+
+#endif  // RS_COMPATIBILITY_LIB
+
+const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
+
+void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir,
+                                            const char *resName,
+                                            const char *nativeLibDir) {
+    void *loaded = nullptr;
+
+#if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
+    std::string scriptSOName = findSharedObjectName(nativeLibDir, resName);
+#else
+    std::string scriptSOName = findSharedObjectName(cacheDir, resName);
+#endif
+
+    // We should check if we can load the library from the standard app
+    // location for shared libraries first.
+    loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName);
+
+    if (loaded == nullptr) {
+        ALOGE("Unable to open shared library (%s): %s",
+              scriptSOName.c_str(), dlerror());
+
+#ifdef RS_COMPATIBILITY_LIB
+        // One final attempt to find the library in "/system/lib".
+        // We do this to allow bundled applications to use the compatibility
+        // library fallback path. Those applications don't have a private
+        // library path, so they need to install to the system directly.
+        // Note that this is really just a testing path.
+        std::string scriptSONameSystem("/system/lib/librs.");
+        scriptSONameSystem.append(resName);
+        scriptSONameSystem.append(".so");
+        loaded = loadSOHelper(scriptSONameSystem.c_str(), cacheDir,
+                              resName);
+        if (loaded == nullptr) {
+            ALOGE("Unable to open system shared library (%s): %s",
+                  scriptSONameSystem.c_str(), dlerror());
+        }
+#endif
+    }
+
+    return loaded;
+}
+
+void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
+                                       const char *resName) {
+    // Keep track of which .so libraries have been loaded. Once a library is
+    // in the set (per-process granularity), we must instead make a copy of
+    // the original shared object (randomly named .so file) and load that one
+    // instead. If we don't do this, we end up aliasing global data between
+    // the various Script instances (which are supposed to be completely
+    // independent).
+    static std::set<std::string> LoadedLibraries;
+
+    void *loaded = nullptr;
+
+    // Skip everything if we don't even have the original library available.
+    if (access(origName, F_OK) != 0) {
+        return nullptr;
+    }
+
+    // Common path is that we have not loaded this Script/library before.
+    if (LoadedLibraries.find(origName) == LoadedLibraries.end()) {
+        loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
+        if (loaded) {
+            LoadedLibraries.insert(origName);
+        }
+        return loaded;
+    }
+
+    std::string newName(cacheDir);
+
+    // Append RS_CACHE_DIR only if it is not found in cacheDir
+    // In driver mode, RS_CACHE_DIR is already appended to cacheDir.
+    if (newName.find(RS_CACHE_DIR) == std::string::npos) {
+        newName.append("/");
+        newName.append(RS_CACHE_DIR);
+        newName.append("/");
+    }
+
+    if (!ensureCacheDirExists(newName.c_str())) {
+        ALOGE("Could not verify or create cache dir: %s", cacheDir);
+        return nullptr;
+    }
+
+    // Construct an appropriately randomized filename for the copy.
+    newName.append("librs.");
+    newName.append(resName);
+    newName.append("#");
+    newName.append(getRandomString(6));  // 62^6 potential filename variants.
+    newName.append(".so");
+
+    int r = copyFile(newName.c_str(), origName);
+    if (r != 0) {
+        ALOGE("Could not create copy %s -> %s", origName, newName.c_str());
+        return nullptr;
+    }
+    loaded = dlopen(newName.c_str(), RTLD_NOW | RTLD_LOCAL);
+    r = unlink(newName.c_str());
+    if (r != 0) {
+        ALOGE("Could not unlink copy %s", newName.c_str());
+    }
+    if (loaded) {
+        LoadedLibraries.insert(newName.c_str());
+    }
+
+    return loaded;
+}
+
+#define MAXLINE 500
+#define MAKE_STR_HELPER(S) #S
+#define MAKE_STR(S) MAKE_STR_HELPER(S)
+#define EXPORT_VAR_STR "exportVarCount: "
+#define EXPORT_FUNC_STR "exportFuncCount: "
+#define EXPORT_FOREACH_STR "exportForEachCount: "
+#define OBJECT_SLOT_STR "objectSlotCount: "
+#define PRAGMA_STR "pragmaCount: "
+#define THREADABLE_STR "isThreadable: "
+
+// Copy up to a newline or size chars from str -> s, updating str
+// Returns s when successful and nullptr when '\0' is finally reached.
+static char* strgets(char *s, int size, const char **ppstr) {
+    if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) {
+        return nullptr;
+    }
+
+    int i;
+    for (i = 0; i < (size - 1); i++) {
+        s[i] = **ppstr;
+        (*ppstr)++;
+        if (s[i] == '\0') {
+            return s;
+        } else if (s[i] == '\n') {
+            s[i+1] = '\0';
+            return s;
+        }
+    }
+
+    // size has been exceeded.
+    s[i] = '\0';
+
+    return s;
+}
+
+ScriptExecutable* ScriptExecutable::createFromSharedObject(
+    Context* RSContext, void* sharedObj) {
+    char line[MAXLINE];
+
+    size_t varCount = 0;
+    size_t funcCount = 0;
+    size_t forEachCount = 0;
+    size_t objectSlotCount = 0;
+    size_t pragmaCount = 0;
+    bool isThreadable = true;
+
+    void** fieldAddress = nullptr;
+    bool* fieldIsObject = nullptr;
+    InvokeFunc_t* invokeFunctions = nullptr;
+    ForEachFunc_t* forEachFunctions = nullptr;
+    uint32_t* forEachSignatures = nullptr;
+    const char ** pragmaKeys = nullptr;
+    const char ** pragmaValues = nullptr;
+
+    const char *rsInfo = (const char *) dlsym(sharedObj, ".rs.info");
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        return nullptr;
+    }
+    if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
+        ALOGE("Invalid export var count!: %s", line);
+        return nullptr;
+    }
+
+    fieldAddress = new void*[varCount];
+    if (fieldAddress == nullptr) {
+        return nullptr;
+    }
+
+    fieldIsObject = new bool[varCount];
+    if (fieldIsObject == nullptr) {
+        goto error;
+    }
+
+    for (size_t i = 0; i < varCount; ++i) {
+        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+            goto error;
+        }
+        char *c = strrchr(line, '\n');
+        if (c) {
+            *c = '\0';
+        }
+        void* addr = dlsym(sharedObj, line);
+        if (addr == nullptr) {
+            ALOGE("Failed to find variable address for %s: %s",
+                  line, dlerror());
+            // Not a critical error if we don't find a global variable.
+        }
+        fieldAddress[i] = addr;
+        fieldIsObject[i] = false;
+    }
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        goto error;
+    }
+    if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
+        ALOGE("Invalid export func count!: %s", line);
+        goto error;
+    }
+
+    invokeFunctions = new InvokeFunc_t[funcCount];
+    if (invokeFunctions == nullptr) {
+        goto error;
+    }
+
+    for (size_t i = 0; i < funcCount; ++i) {
+        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+            goto error;
+        }
+        char *c = strrchr(line, '\n');
+        if (c) {
+            *c = '\0';
+        }
+
+        invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
+        if (invokeFunctions[i] == nullptr) {
+            ALOGE("Failed to get function address for %s(): %s",
+                  line, dlerror());
+            goto error;
+        }
+    }
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        goto error;
+    }
+    if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
+        ALOGE("Invalid export forEach count!: %s", line);
+        goto error;
+    }
+
+    forEachFunctions = new ForEachFunc_t[forEachCount];
+    if (forEachFunctions == nullptr) {
+        goto error;
+    }
+
+    forEachSignatures = new uint32_t[forEachCount];
+    if (forEachSignatures == nullptr) {
+        goto error;
+    }
+
+    for (size_t i = 0; i < forEachCount; ++i) {
+        unsigned int tmpSig = 0;
+        char tmpName[MAXLINE];
+
+        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+            goto error;
+        }
+        if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s",
+                   &tmpSig, tmpName) != 2) {
+          ALOGE("Invalid export forEach!: %s", line);
+          goto error;
+        }
+
+        // Lookup the expanded ForEach kernel.
+        strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName));
+        forEachSignatures[i] = tmpSig;
+        forEachFunctions[i] =
+            (ForEachFunc_t) dlsym(sharedObj, tmpName);
+        if (i != 0 && forEachFunctions[i] == nullptr) {
+            // Ignore missing root.expand functions.
+            // root() is always specified at location 0.
+            ALOGE("Failed to find forEach function address for %s: %s",
+                  tmpName, dlerror());
+            goto error;
+        }
+    }
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        goto error;
+    }
+    if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
+        ALOGE("Invalid object slot count!: %s", line);
+        goto error;
+    }
+
+    for (size_t i = 0; i < objectSlotCount; ++i) {
+        uint32_t varNum = 0;
+        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+            goto error;
+        }
+        if (sscanf(line, "%u", &varNum) != 1) {
+            ALOGE("Invalid object slot!: %s", line);
+            goto error;
+        }
+
+        if (varNum < varCount) {
+            fieldIsObject[varNum] = true;
+        }
+    }
+
+#ifndef RS_COMPATIBILITY_LIB
+    // Do not attempt to read pragmas or isThreadable flag in compat lib path.
+    // Neither is applicable for compat lib
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        goto error;
+    }
+
+    if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
+        ALOGE("Invalid pragma count!: %s", line);
+        goto error;
+    }
+
+    pragmaKeys = new const char*[pragmaCount];
+    if (pragmaKeys == nullptr) {
+        goto error;
+    }
+
+    pragmaValues = new const char*[pragmaCount];
+    if (pragmaValues == nullptr) {
+        goto error;
+    }
+
+    bzero(pragmaKeys, sizeof(char*) * pragmaCount);
+    bzero(pragmaValues, sizeof(char*) * pragmaCount);
+
+    for (size_t i = 0; i < pragmaCount; ++i) {
+        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+            ALOGE("Unable to read pragma at index %zu!", i);
+            goto error;
+        }
+
+        char key[MAXLINE];
+        char value[MAXLINE] = ""; // initialize in case value is empty
+
+        // pragmas can just have a key and no value.  Only check to make sure
+        // that the key is not empty
+        if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s",
+                   key, value) == 0 ||
+            strlen(key) == 0)
+        {
+            ALOGE("Invalid pragma value!: %s", line);
+
+            goto error;
+        }
+
+        char *pKey = new char[strlen(key)+1];
+        strcpy(pKey, key);
+        pragmaKeys[i] = pKey;
+
+        char *pValue = new char[strlen(value)+1];
+        strcpy(pValue, value);
+        pragmaValues[i] = pValue;
+        //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
+    }
+
+    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
+        goto error;
+    }
+
+    char tmpFlag[4];
+    if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) {
+        ALOGE("Invalid threadable flag!: %s", line);
+        goto error;
+    }
+    if (strcmp(tmpFlag, "yes") == 0) {
+        isThreadable = true;
+    } else if (strcmp(tmpFlag, "no") == 0) {
+        isThreadable = false;
+    } else {
+        ALOGE("Invalid threadable flag!: %s", tmpFlag);
+        goto error;
+    }
+
+#endif  // RS_COMPATIBILITY_LIB
+
+    return new ScriptExecutable(
+        RSContext, fieldAddress, fieldIsObject, varCount,
+        invokeFunctions, funcCount,
+        forEachFunctions, forEachSignatures, forEachCount,
+        pragmaKeys, pragmaValues, pragmaCount,
+        isThreadable);
+
+error:
+
+#ifndef RS_COMPATIBILITY_LIB
+    for (size_t idx = 0; idx < pragmaCount; ++idx) {
+        delete [] pragmaKeys[idx];
+        delete [] pragmaValues[idx];
+    }
+
+    delete[] pragmaValues;
+    delete[] pragmaKeys;
+#endif  // RS_COMPATIBILITY_LIB
+
+    delete[] forEachSignatures;
+    delete[] forEachFunctions;
+    delete[] invokeFunctions;
+    delete[] fieldIsObject;
+    delete[] fieldAddress;
+
+    return nullptr;
+}
+
+}  // namespace renderscript
+}  // namespace android
diff --git a/cpu_ref/rsCpuExecutable.h b/cpu_ref/rsCpuExecutable.h
new file mode 100644
index 0000000..acf092c
--- /dev/null
+++ b/cpu_ref/rsCpuExecutable.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RENDERSCRIPT_EXECUTABLE_H
+#define ANDROID_RENDERSCRIPT_EXECUTABLE_H
+
+#include <stdlib.h>
+
+#include "rsCpuScript.h"
+
+namespace android {
+namespace renderscript {
+
+class Context;
+
+class SharedLibraryUtils {
+public:
+#ifndef RS_COMPATIBILITY_LIB
+    static bool createSharedLibrary(const char* cacheDir, const char* resName);
+#endif
+
+    // Load the shared library referred to by cacheDir and resName. If we have
+    // already loaded this library, we instead create a new copy (in the
+    // cache dir) and then load that. We then immediately destroy the copy.
+    // This is required behavior to implement script instancing for the support
+    // library, since shared objects are loaded and de-duped by name only.
+
+    // For 64bit RS Support Lib, the shared lib path cannot be constructed from
+    // cacheDir, so nativeLibDir is needed to load shared libs.
+    static void* loadSharedLibrary(const char *cacheDir, const char *resName,
+                                   const char *nativeLibDir = nullptr);
+
+private:
+    // Attempt to load the shared library from origName, but then fall back to
+    // creating a copy of the shared library if necessary (to ensure instancing).
+    // This function returns the dlopen()-ed handle if successful.
+    static void *loadSOHelper(const char *origName, const char *cacheDir,
+                              const char *resName);
+
+    static const char* LD_EXE_PATH;
+    static const char* RS_CACHE_DIR;
+};
+
+class ScriptExecutable {
+public:
+    ScriptExecutable(Context* RSContext,
+                     void** fieldAddress, bool* fieldIsObject, size_t varCount,
+                     InvokeFunc_t* invokeFunctions, size_t funcCount,
+                     ForEachFunc_t* forEachFunctions, uint32_t* forEachSignatures,
+                     size_t forEachCount,
+                     const char ** pragmaKeys, const char ** pragmaValues,
+                     size_t pragmaCount,
+                     bool isThreadable) :
+        mFieldAddress(fieldAddress), mFieldIsObject(fieldIsObject),
+            mExportedVarCount(varCount),
+            mInvokeFunctions(invokeFunctions), mFuncCount(funcCount),
+            mForEachFunctions(forEachFunctions), mForEachSignatures(forEachSignatures),
+            mForEachCount(forEachCount),
+            mPragmaKeys(pragmaKeys), mPragmaValues(pragmaValues),
+            mPragmaCount(pragmaCount),
+            mIsThreadable(isThreadable), mRS(RSContext) {
+    }
+
+    ~ScriptExecutable() {
+        for (size_t i = 0; i < mExportedVarCount; ++i) {
+            if (mFieldIsObject[i]) {
+                if (mFieldAddress[i] != nullptr) {
+                    rs_object_base *obj_addr =
+                            reinterpret_cast<rs_object_base *>(mFieldAddress[i]);
+                    rsrClearObject(mRS, obj_addr);
+                }
+            }
+        }
+
+        for (size_t i = 0; i < mPragmaCount; ++i) {
+            delete [] mPragmaKeys[i];
+            delete [] mPragmaValues[i];
+        }
+
+        delete[] mPragmaValues;
+        delete[] mPragmaKeys;
+        delete[] mForEachSignatures;
+        delete[] mForEachFunctions;
+        delete[] mInvokeFunctions;
+        delete[] mFieldIsObject;
+        delete[] mFieldAddress;
+    }
+
+    static ScriptExecutable*
+            createFromSharedObject(Context* RSContext, void* sharedObj);
+
+    size_t getExportedVariableCount() const { return mExportedVarCount; }
+    size_t getExportedFunctionCount() const { return mFuncCount; }
+    size_t getExportedForEachCount() const { return mForEachCount; }
+    size_t getPragmaCount() const { return mPragmaCount; }
+
+    void* getFieldAddress(int slot) const { return mFieldAddress[slot]; }
+    bool getFieldIsObject(int slot) const { return mFieldIsObject[slot]; }
+    InvokeFunc_t getInvokeFunction(int slot) const { return mInvokeFunctions[slot]; }
+    ForEachFunc_t getForEachFunction(int slot) const { return mForEachFunctions[slot]; }
+    uint32_t getForEachSignature(int slot) const { return mForEachSignatures[slot]; }
+
+    const char ** getPragmaKeys() const { return mPragmaKeys; }
+    const char ** getPragmaValues() const { return mPragmaValues; }
+
+    bool getThreadable() const { return mIsThreadable; }
+
+private:
+    void** mFieldAddress;
+    bool* mFieldIsObject;
+    size_t mExportedVarCount;
+
+    InvokeFunc_t* mInvokeFunctions;
+    size_t mFuncCount;
+
+    ForEachFunc_t* mForEachFunctions;
+    uint32_t* mForEachSignatures;
+    size_t mForEachCount;
+
+    const char ** mPragmaKeys;
+    const char ** mPragmaValues;
+    size_t mPragmaCount;
+
+    bool mIsThreadable;
+
+    Context* mRS;
+};
+
+}  // namespace renderscript
+}  // namespace android
+
+#endif  // ANDROID_RENDERSCRIPT_EXECUTABLE_H
diff --git a/cpu_ref/rsCpuScript.cpp b/cpu_ref/rsCpuScript.cpp
index 1c7fdac..af8b640 100644
--- a/cpu_ref/rsCpuScript.cpp
+++ b/cpu_ref/rsCpuScript.cpp
@@ -16,6 +16,7 @@
 
 #include "rsCpuCore.h"
 #include "rsCpuScript.h"
+#include "rsCpuExecutable.h"
 
 #ifdef RS_COMPATIBILITY_LIB
     #include <stdio.h>
@@ -41,7 +42,6 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <string.h>
-#include <fstream>
 #include <iostream>
 
 #ifdef __LP64__
@@ -51,90 +51,6 @@
 #endif
 
 namespace {
-
-// Create a len length string containing random characters from [A-Za-z0-9].
-static std::string getRandomString(size_t len) {
-    char buf[len + 1];
-    for (size_t i = 0; i < len; i++) {
-        uint32_t r = arc4random() & 0xffff;
-        r %= 62;
-        if (r < 26) {
-            // lowercase
-            buf[i] = 'a' + r;
-        } else if (r < 52) {
-            // uppercase
-            buf[i] = 'A' + (r - 26);
-        } else {
-            // Use a number
-            buf[i] = '0' + (r - 52);
-        }
-    }
-    buf[len] = '\0';
-    return std::string(buf);
-}
-
-// Check if a path exists and attempt to create it if it doesn't.
-static bool ensureCacheDirExists(const char *path) {
-    if (access(path, R_OK | W_OK | X_OK) == 0) {
-        // Done if we can rwx the directory
-        return true;
-    }
-    if (mkdir(path, 0700) == 0) {
-        return true;
-    }
-    return false;
-}
-
-// Copy the file named \p srcFile to \p dstFile.
-// Return 0 on success and -1 if anything wasn't copied.
-static int copyFile(const char *dstFile, const char *srcFile) {
-    std::ifstream srcStream(srcFile);
-    if (!srcStream) {
-        ALOGE("Could not verify or read source file: %s", srcFile);
-        return -1;
-    }
-    std::ofstream dstStream(dstFile);
-    if (!dstStream) {
-        ALOGE("Could not verify or write destination file: %s", dstFile);
-        return -1;
-    }
-    dstStream << srcStream.rdbuf();
-    if (!dstStream) {
-        ALOGE("Could not write destination file: %s", dstFile);
-        return -1;
-    }
-
-    srcStream.close();
-    dstStream.close();
-
-    return 0;
-}
-
-static std::string findSharedObjectName(const char *cacheDir,
-                                        const char *resName) {
-#ifndef RS_SERVER
-    std::string scriptSOName(cacheDir);
-#if defined(RS_COMPATIBILITY_LIB) && !defined(__LP64__)
-    size_t cutPos = scriptSOName.rfind("cache");
-    if (cutPos != std::string::npos) {
-        scriptSOName.erase(cutPos);
-    } else {
-        ALOGE("Found peculiar cacheDir (missing \"cache\"): %s", cacheDir);
-    }
-    scriptSOName.append("/lib/librs.");
-#else
-    scriptSOName.append("/librs.");
-#endif // RS_COMPATIBILITY_LIB
-
-#else
-    std::string scriptSOName("lib");
-#endif // RS_SERVER
-    scriptSOName.append(resName);
-    scriptSOName.append(".so");
-
-    return scriptSOName;
-}
-
 #ifndef RS_COMPATIBILITY_LIB
 
 static bool is_force_recompile() {
@@ -256,228 +172,12 @@
     }
 }
 
-std::string getCommandLine(int argc, const char* const* argv) {
-    std::string s;
-    for (int i = 0; i < argc; i++) {
-        if (i > 0) {
-            s += ' ';
-        }
-        s += argv[i];
-    }
-    return s;
-}
-
 #endif  // !defined(RS_COMPATIBILITY_LIB)
 }  // namespace
 
 namespace android {
 namespace renderscript {
 
-const char* SharedLibraryUtils::LD_EXE_PATH = "/system/bin/ld.mc";
-const char* SharedLibraryUtils::RS_CACHE_DIR = "com.android.renderscript.cache";
-
-#ifndef RS_COMPATIBILITY_LIB
-
-bool SharedLibraryUtils::createSharedLibrary(const char *cacheDir, const char *resName) {
-    std::string sharedLibName = findSharedObjectName(cacheDir, resName);
-    std::string objFileName = cacheDir;
-    objFileName.append("/");
-    objFileName.append(resName);
-    objFileName.append(".o");
-
-    const char *compiler_rt = SYSLIBPATH"/libcompiler_rt.so";
-    std::vector<const char *> args = {
-        LD_EXE_PATH,
-        "-shared",
-        "-nostdlib",
-        compiler_rt,
-        "-mtriple", DEFAULT_TARGET_TRIPLE_STRING,
-        "-L", SYSLIBPATH,
-        "-lRSDriver", "-lm", "-lc",
-        objFileName.c_str(),
-        "-o", sharedLibName.c_str(),
-        nullptr
-    };
-
-    std::string cmdLineStr = getCommandLine(args.size()-1, args.data());
-
-    pid_t pid = fork();
-
-    switch (pid) {
-    case -1: {  // Error occurred (we attempt no recovery)
-        ALOGE("Couldn't fork for linker (%s) execution", LD_EXE_PATH);
-        return false;
-    }
-    case 0: {  // Child process
-        ALOGV("Invoking ld.mc with args '%s'", cmdLineStr.c_str());
-        execv(LD_EXE_PATH, (char* const*) args.data());
-
-        ALOGE("execv() failed: %s", strerror(errno));
-        abort();
-        return false;
-    }
-    default: {  // Parent process (actual driver)
-        // Wait on child process to finish compiling the source.
-        int status = 0;
-        pid_t w = waitpid(pid, &status, 0);
-        if (w == -1) {
-            ALOGE("Could not wait for linker (%s)", LD_EXE_PATH);
-            return false;
-        }
-
-        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-            return true;
-        }
-
-        ALOGE("Linker (%s) terminated unexpectedly", LD_EXE_PATH);
-        return false;
-    }
-    }
-}
-
-#endif  // RS_COMPATIBILITY_LIB
-
-
-void* SharedLibraryUtils::loadSharedLibrary(const char *cacheDir, const char *resName, const char *nativeLibDir) {
-    void *loaded = nullptr;
-
-#if defined(RS_COMPATIBILITY_LIB) && defined(__LP64__)
-    std::string scriptSOName = findSharedObjectName(nativeLibDir, resName);
-#else
-    std::string scriptSOName = findSharedObjectName(cacheDir, resName);
-#endif
-
-    // We should check if we can load the library from the standard app
-    // location for shared libraries first.
-    loaded = loadSOHelper(scriptSOName.c_str(), cacheDir, resName);
-
-    if (loaded == nullptr) {
-        ALOGE("Unable to open shared library (%s): %s",
-              scriptSOName.c_str(), dlerror());
-
-#ifdef RS_COMPATIBILITY_LIB
-        // One final attempt to find the library in "/system/lib".
-        // We do this to allow bundled applications to use the compatibility
-        // library fallback path. Those applications don't have a private
-        // library path, so they need to install to the system directly.
-        // Note that this is really just a testing path.
-        std::string scriptSONameSystem("/system/lib/librs.");
-        scriptSONameSystem.append(resName);
-        scriptSONameSystem.append(".so");
-        loaded = loadSOHelper(scriptSONameSystem.c_str(), cacheDir,
-                              resName);
-        if (loaded == nullptr) {
-            ALOGE("Unable to open system shared library (%s): %s",
-                  scriptSONameSystem.c_str(), dlerror());
-        }
-#endif
-    }
-
-    return loaded;
-}
-
-void* SharedLibraryUtils::loadSOHelper(const char *origName, const char *cacheDir,
-                                       const char *resName) {
-    // Keep track of which .so libraries have been loaded. Once a library is
-    // in the set (per-process granularity), we must instead make a copy of
-    // the original shared object (randomly named .so file) and load that one
-    // instead. If we don't do this, we end up aliasing global data between
-    // the various Script instances (which are supposed to be completely
-    // independent).
-    static std::set<std::string> LoadedLibraries;
-
-    void *loaded = nullptr;
-
-    // Skip everything if we don't even have the original library available.
-    if (access(origName, F_OK) != 0) {
-        return nullptr;
-    }
-
-    // Common path is that we have not loaded this Script/library before.
-    if (LoadedLibraries.find(origName) == LoadedLibraries.end()) {
-        loaded = dlopen(origName, RTLD_NOW | RTLD_LOCAL);
-        if (loaded) {
-            LoadedLibraries.insert(origName);
-        }
-        return loaded;
-    }
-
-    std::string newName(cacheDir);
-
-    // Append RS_CACHE_DIR only if it is not found in cacheDir
-    // In driver mode, RS_CACHE_DIR is already appended to cacheDir.
-    if (newName.find(RS_CACHE_DIR) == std::string::npos) {
-        newName.append("/");
-        newName.append(RS_CACHE_DIR);
-        newName.append("/");
-    }
-
-    if (!ensureCacheDirExists(newName.c_str())) {
-        ALOGE("Could not verify or create cache dir: %s", cacheDir);
-        return nullptr;
-    }
-
-    // Construct an appropriately randomized filename for the copy.
-    newName.append("librs.");
-    newName.append(resName);
-    newName.append("#");
-    newName.append(getRandomString(6));  // 62^6 potential filename variants.
-    newName.append(".so");
-
-    int r = copyFile(newName.c_str(), origName);
-    if (r != 0) {
-        ALOGE("Could not create copy %s -> %s", origName, newName.c_str());
-        return nullptr;
-    }
-    loaded = dlopen(newName.c_str(), RTLD_NOW | RTLD_LOCAL);
-    r = unlink(newName.c_str());
-    if (r != 0) {
-        ALOGE("Could not unlink copy %s", newName.c_str());
-    }
-    if (loaded) {
-        LoadedLibraries.insert(newName.c_str());
-    }
-
-    return loaded;
-}
-
-const char* RsdCpuScriptImpl::BCC_EXE_PATH = "/system/bin/bcc";
-
-#define MAXLINE 500
-#define MAKE_STR_HELPER(S) #S
-#define MAKE_STR(S) MAKE_STR_HELPER(S)
-#define EXPORT_VAR_STR "exportVarCount: "
-#define EXPORT_FUNC_STR "exportFuncCount: "
-#define EXPORT_FOREACH_STR "exportForEachCount: "
-#define OBJECT_SLOT_STR "objectSlotCount: "
-#define PRAGMA_STR "pragmaCount: "
-#define THREADABLE_STR "isThreadable: "
-
-// Copy up to a newline or size chars from str -> s, updating str
-// Returns s when successful and nullptr when '\0' is finally reached.
-static char* strgets(char *s, int size, const char **ppstr) {
-    if (!ppstr || !*ppstr || **ppstr == '\0' || size < 1) {
-        return nullptr;
-    }
-
-    int i;
-    for (i = 0; i < (size - 1); i++) {
-        s[i] = **ppstr;
-        (*ppstr)++;
-        if (s[i] == '\0') {
-            return s;
-        } else if (s[i] == '\n') {
-            s[i+1] = '\0';
-            return s;
-        }
-    }
-
-    // size has been exceeded.
-    s[i] = '\0';
-
-    return s;
-}
-
 RsdCpuScriptImpl::RsdCpuScriptImpl(RsdCpuReferenceImpl *ctx, const Script *s) {
     mCtx = ctx;
     mScript = s;
@@ -537,265 +237,6 @@
     return true;
 }
 
-ScriptExecutable* ScriptExecutable::createFromSharedObject(
-    Context* RSContext, void* sharedObj) {
-    char line[MAXLINE];
-
-    size_t varCount = 0;
-    size_t funcCount = 0;
-    size_t forEachCount = 0;
-    size_t objectSlotCount = 0;
-    size_t pragmaCount = 0;
-    bool isThreadable = true;
-
-    void** fieldAddress = nullptr;
-    bool* fieldIsObject = nullptr;
-    InvokeFunc_t* invokeFunctions = nullptr;
-    ForEachFunc_t* forEachFunctions = nullptr;
-    uint32_t* forEachSignatures = nullptr;
-    const char ** pragmaKeys = nullptr;
-    const char ** pragmaValues = nullptr;
-
-    const char *rsInfo = (const char *) dlsym(sharedObj, ".rs.info");
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        return nullptr;
-    }
-    if (sscanf(line, EXPORT_VAR_STR "%zu", &varCount) != 1) {
-        ALOGE("Invalid export var count!: %s", line);
-        return nullptr;
-    }
-
-    fieldAddress = new void*[varCount];
-    if (fieldAddress == nullptr) {
-        return nullptr;
-    }
-
-    fieldIsObject = new bool[varCount];
-    if (fieldIsObject == nullptr) {
-        goto error;
-    }
-
-    for (size_t i = 0; i < varCount; ++i) {
-        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-            goto error;
-        }
-        char *c = strrchr(line, '\n');
-        if (c) {
-            *c = '\0';
-        }
-        void* addr = dlsym(sharedObj, line);
-        if (addr == nullptr) {
-            ALOGE("Failed to find variable address for %s: %s",
-                  line, dlerror());
-            // Not a critical error if we don't find a global variable.
-        }
-        fieldAddress[i] = addr;
-        fieldIsObject[i] = false;
-    }
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        goto error;
-    }
-    if (sscanf(line, EXPORT_FUNC_STR "%zu", &funcCount) != 1) {
-        ALOGE("Invalid export func count!: %s", line);
-        goto error;
-    }
-
-    invokeFunctions = new InvokeFunc_t[funcCount];
-    if (invokeFunctions == nullptr) {
-        goto error;
-    }
-
-    for (size_t i = 0; i < funcCount; ++i) {
-        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-            goto error;
-        }
-        char *c = strrchr(line, '\n');
-        if (c) {
-            *c = '\0';
-        }
-
-        invokeFunctions[i] = (InvokeFunc_t) dlsym(sharedObj, line);
-        if (invokeFunctions[i] == nullptr) {
-            ALOGE("Failed to get function address for %s(): %s",
-                  line, dlerror());
-            goto error;
-        }
-    }
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        goto error;
-    }
-    if (sscanf(line, EXPORT_FOREACH_STR "%zu", &forEachCount) != 1) {
-        ALOGE("Invalid export forEach count!: %s", line);
-        goto error;
-    }
-
-    forEachFunctions = new ForEachFunc_t[forEachCount];
-    if (forEachFunctions == nullptr) {
-        goto error;
-    }
-
-    forEachSignatures = new uint32_t[forEachCount];
-    if (forEachSignatures == nullptr) {
-        goto error;
-    }
-
-    for (size_t i = 0; i < forEachCount; ++i) {
-        unsigned int tmpSig = 0;
-        char tmpName[MAXLINE];
-
-        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-            goto error;
-        }
-        if (sscanf(line, "%u - %" MAKE_STR(MAXLINE) "s",
-                   &tmpSig, tmpName) != 2) {
-          ALOGE("Invalid export forEach!: %s", line);
-          goto error;
-        }
-
-        // Lookup the expanded ForEach kernel.
-        strncat(tmpName, ".expand", MAXLINE-1-strlen(tmpName));
-        forEachSignatures[i] = tmpSig;
-        forEachFunctions[i] =
-            (ForEachFunc_t) dlsym(sharedObj, tmpName);
-        if (i != 0 && forEachFunctions[i] == nullptr) {
-            // Ignore missing root.expand functions.
-            // root() is always specified at location 0.
-            ALOGE("Failed to find forEach function address for %s: %s",
-                  tmpName, dlerror());
-            goto error;
-        }
-    }
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        goto error;
-    }
-    if (sscanf(line, OBJECT_SLOT_STR "%zu", &objectSlotCount) != 1) {
-        ALOGE("Invalid object slot count!: %s", line);
-        goto error;
-    }
-
-    for (size_t i = 0; i < objectSlotCount; ++i) {
-        uint32_t varNum = 0;
-        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-            goto error;
-        }
-        if (sscanf(line, "%u", &varNum) != 1) {
-            ALOGE("Invalid object slot!: %s", line);
-            goto error;
-        }
-
-        if (varNum < varCount) {
-            fieldIsObject[varNum] = true;
-        }
-    }
-
-#ifndef RS_COMPATIBILITY_LIB
-    // Do not attempt to read pragmas or isThreadable flag in compat lib path.
-    // Neither is applicable for compat lib
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        goto error;
-    }
-
-    if (sscanf(line, PRAGMA_STR "%zu", &pragmaCount) != 1) {
-        ALOGE("Invalid pragma count!: %s", line);
-        goto error;
-    }
-
-    pragmaKeys = new const char*[pragmaCount];
-    if (pragmaKeys == nullptr) {
-        goto error;
-    }
-
-    pragmaValues = new const char*[pragmaCount];
-    if (pragmaValues == nullptr) {
-        goto error;
-    }
-
-    bzero(pragmaKeys, sizeof(char*) * pragmaCount);
-    bzero(pragmaValues, sizeof(char*) * pragmaCount);
-
-    for (size_t i = 0; i < pragmaCount; ++i) {
-        if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-            ALOGE("Unable to read pragma at index %zu!", i);
-            goto error;
-        }
-
-        char key[MAXLINE];
-        char value[MAXLINE] = ""; // initialize in case value is empty
-
-        // pragmas can just have a key and no value.  Only check to make sure
-        // that the key is not empty
-        if (sscanf(line, "%" MAKE_STR(MAXLINE) "s - %" MAKE_STR(MAXLINE) "s",
-                   key, value) == 0 ||
-            strlen(key) == 0)
-        {
-            ALOGE("Invalid pragma value!: %s", line);
-
-            goto error;
-        }
-
-        char *pKey = new char[strlen(key)+1];
-        strcpy(pKey, key);
-        pragmaKeys[i] = pKey;
-
-        char *pValue = new char[strlen(value)+1];
-        strcpy(pValue, value);
-        pragmaValues[i] = pValue;
-        //ALOGE("Pragma %zu: Key: '%s' Value: '%s'", i, pKey, pValue);
-    }
-
-    if (strgets(line, MAXLINE, &rsInfo) == nullptr) {
-        goto error;
-    }
-
-    char tmpFlag[4];
-    if (sscanf(line, THREADABLE_STR "%4s", tmpFlag) != 1) {
-        ALOGE("Invalid threadable flag!: %s", line);
-        goto error;
-    }
-    if (strcmp(tmpFlag, "yes") == 0) {
-        isThreadable = true;
-    } else if (strcmp(tmpFlag, "no") == 0) {
-        isThreadable = false;
-    } else {
-        ALOGE("Invalid threadable flag!: %s", tmpFlag);
-        goto error;
-    }
-
-#endif  // RS_COMPATIBILITY_LIB
-
-    return new ScriptExecutable(
-        RSContext, fieldAddress, fieldIsObject, varCount,
-        invokeFunctions, funcCount,
-        forEachFunctions, forEachSignatures, forEachCount,
-        pragmaKeys, pragmaValues, pragmaCount,
-        isThreadable);
-
-error:
-
-#ifndef RS_COMPATIBILITY_LIB
-    for (size_t idx = 0; idx < pragmaCount; ++idx) {
-        delete [] pragmaKeys[idx];
-        delete [] pragmaValues[idx];
-    }
-
-    delete[] pragmaValues;
-    delete[] pragmaKeys;
-#endif  // RS_COMPATIBILITY_LIB
-
-    delete[] forEachSignatures;
-    delete[] forEachFunctions;
-    delete[] invokeFunctions;
-    delete[] fieldIsObject;
-    delete[] fieldAddress;
-
-    return nullptr;
-}
-
 bool RsdCpuScriptImpl::init(char const *resName, char const *cacheDir,
                             uint8_t const *bitcode, size_t bitcodeSize,
                             uint32_t flags, char const *bccPluginName) {
@@ -846,8 +287,9 @@
     setCompileArguments(&compileArguments, bcFileName, cacheDir, resName, core_lib,
                         useRSDebugContext, bccPluginName);
     // The last argument of compileArguments ia a nullptr, so remove 1 from the size.
-    std::string compileCommandLine =
-                getCommandLine(compileArguments.size() - 1, compileArguments.data());
+    std::unique_ptr<const char> joined(
+        rsuJoinStrings(compileArguments.size() - 1, compileArguments.data()));
+    std::string compileCommandLine (joined.get());
 
     if (!is_force_recompile() && !useRSDebugContext) {
         mScriptSO = SharedLibraryUtils::loadSharedLibrary(cacheDir, resName);
diff --git a/cpu_ref/rsCpuScript.h b/cpu_ref/rsCpuScript.h
index a775c73..03aabf4 100644
--- a/cpu_ref/rsCpuScript.h
+++ b/cpu_ref/rsCpuScript.h
@@ -38,118 +38,7 @@
 namespace android {
 namespace renderscript {
 
-class SharedLibraryUtils {
- public:
-#ifndef RS_COMPATIBILITY_LIB
-  static bool createSharedLibrary(const char* cacheDir, const char* resName);
-#endif
-
-  // Load the shared library referred to by cacheDir and resName. If we have
-  // already loaded this library, we instead create a new copy (in the
-  // cache dir) and then load that. We then immediately destroy the copy.
-  // This is required behavior to implement script instancing for the support
-  // library, since shared objects are loaded and de-duped by name only.
-
-  // For 64bit RS Support Lib, the shared lib path cannot be constructed from
-  // cacheDir, so nativeLibDir is needed to load shared libs.
-  static void* loadSharedLibrary(const char *cacheDir, const char *resName,
-                                 const char *nativeLibDir = nullptr);
-
- private:
-  // Attempt to load the shared library from origName, but then fall back to
-  // creating a copy of the shared library if necessary (to ensure instancing).
-  // This function returns the dlopen()-ed handle if successful.
-  static void *loadSOHelper(const char *origName, const char *cacheDir,
-                            const char *resName);
-
-  static const char* LD_EXE_PATH;
-  static const char* RS_CACHE_DIR;
-};
-
-class ScriptExecutable {
- public:
-  ScriptExecutable(Context* RSContext,
-                   void** fieldAddress, bool* fieldIsObject, size_t varCount,
-                   InvokeFunc_t* invokeFunctions, size_t funcCount,
-                   ForEachFunc_t* forEachFunctions, uint32_t* forEachSignatures,
-                   size_t forEachCount,
-                   const char ** pragmaKeys, const char ** pragmaValues,
-                   size_t pragmaCount,
-                   bool isThreadable) :
-      mFieldAddress(fieldAddress), mFieldIsObject(fieldIsObject),
-      mExportedVarCount(varCount),
-      mInvokeFunctions(invokeFunctions), mFuncCount(funcCount),
-      mForEachFunctions(forEachFunctions), mForEachSignatures(forEachSignatures),
-      mForEachCount(forEachCount),
-      mPragmaKeys(pragmaKeys), mPragmaValues(pragmaValues),
-      mPragmaCount(pragmaCount),
-      mIsThreadable(isThreadable), mRS(RSContext) {
-  }
-
-  ~ScriptExecutable() {
-      for (size_t i = 0; i < mExportedVarCount; ++i) {
-          if (mFieldIsObject[i]) {
-              if (mFieldAddress[i] != nullptr) {
-                  rs_object_base *obj_addr =
-                      reinterpret_cast<rs_object_base *>(mFieldAddress[i]);
-                  rsrClearObject(mRS, obj_addr);
-              }
-          }
-      }
-
-      for (size_t i = 0; i < mPragmaCount; ++i) {
-          delete [] mPragmaKeys[i];
-          delete [] mPragmaValues[i];
-      }
-
-      delete[] mPragmaValues;
-      delete[] mPragmaKeys;
-      delete[] mForEachSignatures;
-      delete[] mForEachFunctions;
-      delete[] mInvokeFunctions;
-      delete[] mFieldIsObject;
-      delete[] mFieldAddress;
-  }
-
-  static ScriptExecutable*
-  createFromSharedObject(Context* RSContext, void* sharedObj);
-
-  size_t getExportedVariableCount() const { return mExportedVarCount; }
-  size_t getExportedFunctionCount() const { return mFuncCount; }
-  size_t getExportedForEachCount() const { return mForEachCount; }
-  size_t getPragmaCount() const { return mPragmaCount; }
-
-  void* getFieldAddress(int slot) const { return mFieldAddress[slot]; }
-  bool getFieldIsObject(int slot) const { return mFieldIsObject[slot]; }
-  InvokeFunc_t getInvokeFunction(int slot) const { return mInvokeFunctions[slot]; }
-  ForEachFunc_t getForEachFunction(int slot) const { return mForEachFunctions[slot]; }
-  uint32_t getForEachSignature(int slot) const { return mForEachSignatures[slot]; }
-
-  const char ** getPragmaKeys() const { return mPragmaKeys; }
-  const char ** getPragmaValues() const { return mPragmaValues; }
-
-  bool getThreadable() const { return mIsThreadable; }
-
- private:
-  void** mFieldAddress;
-  bool* mFieldIsObject;
-  size_t mExportedVarCount;
-
-  InvokeFunc_t* mInvokeFunctions;
-  size_t mFuncCount;
-
-  ForEachFunc_t* mForEachFunctions;
-  uint32_t* mForEachSignatures;
-  size_t mForEachCount;
-
-  const char ** mPragmaKeys;
-  const char ** mPragmaValues;
-  size_t mPragmaCount;
-
-  bool mIsThreadable;
-
-  Context* mRS;
-};
+class ScriptExecutable;
 
 class RsdCpuScriptImpl : public RsdCpuReferenceImpl::CpuScript {
 public:
@@ -240,12 +129,12 @@
     void * mIntrinsicData;
     bool mIsThreadable;
 
- public:
-  static const char* BCC_EXE_PATH;
-  const char* getBitcodeFilePath() const { return mBitcodeFilePath.string(); }
+public:
+    static const char* BCC_EXE_PATH;
+    const char* getBitcodeFilePath() const { return mBitcodeFilePath.string(); }
 
- private:
-  String8 mBitcodeFilePath;
+private:
+    String8 mBitcodeFilePath;
 };
 
 Allocation * rsdScriptGetAllocationForPointer(
diff --git a/cpu_ref/rsCpuScriptGroup2.cpp b/cpu_ref/rsCpuScriptGroup2.cpp
index 80c46a0..bd47403 100644
--- a/cpu_ref/rsCpuScriptGroup2.cpp
+++ b/cpu_ref/rsCpuScriptGroup2.cpp
@@ -14,6 +14,7 @@
 #include "rsClosure.h"
 #include "rsContext.h"
 #include "rsCpuCore.h"
+#include "rsCpuExecutable.h"
 #include "rsCpuScript.h"
 #include "rsScript.h"
 #include "rsScriptGroup2.h"
@@ -210,16 +211,6 @@
     args->push_back(nullptr);
 }
 
-string convertListToString(int n, const char* const* strs) {
-    string ret;
-    ret.append(strs[0]);
-    for (int i = 1; i < n; i++) {
-        ret.append(" ");
-        ret.append(strs[i]);
-    }
-    return ret;
-}
-
 bool fuseAndCompile(const char** arguments,
                     const string& commandLine) {
     const pid_t pid = fork();
@@ -296,8 +287,9 @@
     vector<const char*> arguments;
     setupCompileArguments(inputFiles, slots, cacheDir, outputFileName, rsLibPath,
                           &arguments);
-    string commandLine =
-            convertListToString(arguments.size() - 1, arguments.data());
+    std::unique_ptr<const char> joined(
+        rsuJoinStrings(arguments.size() - 1, arguments.data()));
+    string commandLine (joined.get());
 
     if (!fuseAndCompile(arguments.data(), commandLine)) {
         return;
diff --git a/rsCppUtils.cpp b/rsCppUtils.cpp
index 79cb4d3..c9a19c2 100644
--- a/rsCppUtils.cpp
+++ b/rsCppUtils.cpp
@@ -17,6 +17,8 @@
 #include "rsUtils.h"
 #include "rsCppUtils.h"
 
+#include <string>
+
 #include <string.h>
 
 namespace android {
@@ -33,6 +35,16 @@
     return n;
 }
 
+const char* rsuJoinStrings(int n, const char* const* strs) {
+    std::string tmp;
+    for (int i = 0; i < n; i++) {
+        if (i > 0) {
+            tmp.append(" ");
+        }
+        tmp.append(strs[i]);
+    }
+    return strndup(tmp.c_str(), tmp.size());
+}
 
 }
 }
diff --git a/rsCppUtils.h b/rsCppUtils.h
index 1f792bc..cc6d6cf 100644
--- a/rsCppUtils.h
+++ b/rsCppUtils.h
@@ -180,7 +180,6 @@
     return secs/1000000;
 }
 
-
 #endif // RS_SERVER || RS_COMPATIBILITY_LIB
 
 namespace android {
@@ -282,6 +281,8 @@
     return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24);
 }
 
+const char* rsuJoinStrings(int n, const char* const* strs);
+
 }
 }