/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * 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. */ #include #include #include #include "log.h" #include "utils.h" #include "pkgmgr_parser_plugin_interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ni_common.h" #include "path_manager.h" #include "plugin_manager.h" #ifdef LOG_TAG #undef LOG_TAG #endif #define LOG_TAG "NETCORE_INSTALLER_PLUGIN" #ifndef CROSSGEN_PATH #error "CROSSGEN_PATH is missed" #endif #define __XSTR(x) #x #define __STR(x) __XSTR(x) static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH); #undef __STR #undef __XSTR static int __interval = 0; static std::string __tpa; static void waitInterval() { // by the recommand, ignore small value for performance. if (__interval > 10000) { fprintf(stderr, "sleep %d usec\n", __interval); usleep(__interval); } } static void updateNiFileInfo(const std::string& dllPath, const std::string& niPath) { char* label = NULL; // change smack label if (smack_getlabel(dllPath.c_str(), &label, SMACK_LABEL_ACCESS) == 0) { if (smack_setlabel(niPath.c_str(), label, SMACK_LABEL_ACCESS) < 0) { fprintf(stderr, "Fail to set smack label\n"); } free(label); } // change owner and groups for generated ni file. struct stat info; if (!stat(dllPath.c_str(), &info)) { if (chown(niPath.c_str(), info.st_uid, info.st_gid) == -1) fprintf(stderr, "Failed to change owner and group name\n"); } } static std::string getNiFilePath(const std::string& dllPath) { size_t index = dllPath.find_last_of("."); if (index == std::string::npos) { fprintf(stderr, "File doesnot contain extension. fail to get NI file name\n"); return ""; } std::string fName = dllPath.substr(0, index); std::string fExt = dllPath.substr(index, dllPath.length()); // crossgen generate file with lower case extension only std::transform(fExt.begin(), fExt.end(), fExt.begin(), ::tolower); std::string niPath = fName + ".ni" + fExt; return niPath; } static std::string getAppNIPath(const std::string& niPath) { size_t index = niPath.find_last_of("/"); if (index == std::string::npos) { fprintf(stderr, "dllPath doesnot contains path info\n"); return ""; } std::string prevPath = niPath.substr(0, index); std::string fileName = niPath.substr(index, niPath.length()); std::string niDirPath = prevPath + APP_NI_SUB_DIR; if (!isFileExist(niDirPath)) { if (mkdir(niDirPath.c_str(), 0755) == 0) { updateNiFileInfo(prevPath, niDirPath); } else { fprintf(stderr, "Fail to create app ni directory (%s)\n", niDirPath.c_str()); } } return niDirPath + fileName; } static bool niExist(const std::string& path) { std::string f = getNiFilePath(path); if (f.empty()) { return false; } if (isFileExist(f)) { return true; } // native image of System.Private.CoreLib.dll should have to overwrite // original file to support new coreclr if (path.find("System.Private.CoreLib.dll") != std::string::npos) { std::string coreLibBackup = path + ".Backup"; if (isFileExist(coreLibBackup)) { return true; } } return false; } static ni_error_e crossgen(const std::string& dllPath, const std::string& appPath, bool enableR2R, bool isAppNI = false) { if (!isFileExist(dllPath)) { fprintf(stderr, "dll file is not exist : %s\n", dllPath.c_str()); return NI_ERROR_NO_SUCH_FILE; } if (!isManagedAssembly(dllPath)) { fprintf(stderr, "Input file is not a dll file : %s\n", dllPath.c_str()); return NI_ERROR_INVALID_PARAMETER; } if (niExist(dllPath)) { fprintf(stderr, "Already ni file is exist for %s\n", dllPath.c_str()); return NI_ERROR_ALREADY_EXIST; } std::string absDllPath = absolutePath(dllPath); std::string absNiPath = getNiFilePath(dllPath); if (absNiPath.empty()) { fprintf(stderr, "Fail to get ni file name\n"); return NI_ERROR_UNKNOWN; } if (isAppNI) { absNiPath = getAppNIPath(absNiPath); } pid_t pid = fork(); if (pid == -1) return NI_ERROR_UNKNOWN; if (pid > 0) { int status; waitpid(pid, &status, 0); if (WIFEXITED(status)) { // Do not use niExist() function to check whether ni file created or not. // niEixst() return false for System.Private.Corelib.dll if (isFileExist(absNiPath)) { updateNiFileInfo(absDllPath, absNiPath); return NI_ERROR_NONE; } else { fprintf(stderr, "Fail to create native image for %s\n", dllPath.c_str()); return NI_ERROR_NO_SUCH_FILE; } } } else { std::string jitPath = getRuntimeDir() + "/libclrjit.so"; std::vector argv = { __CROSSGEN_PATH, "/nologo", "/Trusted_Platform_Assemblies", __tpa.c_str(), "/JITPath", jitPath.c_str() }; if (!enableR2R) { argv.push_back("/FragileNonVersionable"); } argv.push_back("/App_Paths"); std::string absAppPath; if (!appPath.empty()) { absAppPath = appPath; } else { absAppPath = baseName(absDllPath); } argv.push_back(absAppPath.c_str()); argv.push_back("/out"); argv.push_back(absNiPath.c_str()); argv.push_back(absDllPath.c_str()); argv.push_back(nullptr); fprintf(stderr, "+ %s (%s)\n", absDllPath.c_str(), enableR2R ? "R2R" : "FNV"); execv(__CROSSGEN_PATH, const_cast(argv.data())); exit(0); } return NI_ERROR_NONE; } static ni_error_e getRootPath(std::string pkgId, std::string& rootPath) { int ret = 0; char *path = 0; uid_t uid = 0; if (pkgmgr_installer_info_get_target_uid(&uid) < 0) { _ERR("Failed to get UID"); return NI_ERROR_UNKNOWN; } pkgmgrinfo_pkginfo_h handle; if (uid == 0) { ret = pkgmgrinfo_pkginfo_get_pkginfo(pkgId.c_str(), &handle); if (ret != PMINFO_R_OK) return NI_ERROR_UNKNOWN; } else { ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgId.c_str(), uid, &handle); if (ret != PMINFO_R_OK) return NI_ERROR_UNKNOWN; } ret = pkgmgrinfo_pkginfo_get_root_path(handle, &path); if (ret != PMINFO_R_OK) { pkgmgrinfo_pkginfo_destroy_pkginfo(handle); return NI_ERROR_UNKNOWN; } rootPath = path; pkgmgrinfo_pkginfo_destroy_pkginfo(handle); return NI_ERROR_NONE; } // callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach" static int appAotCb(pkgmgrinfo_appinfo_h handle, void *userData) { char *pkgId = NULL; int ret = 0; bool* enableR2R = (bool*)userData; ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId); if (ret != PMINFO_R_OK) { fprintf(stderr, "Failed to get pkgid\n"); return -1; } if (removeNiUnderPkgRoot(pkgId) != 0) { fprintf(stderr, "Failed to remove previous dlls from [%s]\n", pkgId); return -1; } // Regenerate ni files with R2R mode forcibiliy. (there is no way to now which option is used) if (createNiUnderPkgRoot(pkgId, *enableR2R) != 0) { fprintf(stderr, "Failed to get root path from [%s]\n", pkgId); return -1; } else { fprintf(stderr, "Complete make application to native image\n"); } return 0; } static void createCoreLibNI(bool enableR2R) { std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll"); std::string niCoreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.ni.dll"); std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup"); if (!isFileExist(coreLibBackup)) { if (!crossgen(coreLib, std::string(), enableR2R)) { if (rename(coreLib.c_str(), coreLibBackup.c_str())) { fprintf(stderr, "Failed to rename System.Private.CoreLib.dll\n"); } if (rename(niCoreLib.c_str(), coreLib.c_str())) { fprintf(stderr, "Failed to rename System.Private.CoreLib.ni.dll\n"); } } else { fprintf(stderr, "Failed to create native image for %s\n", coreLib.c_str()); } } } ni_error_e initNICommon(NiCommonOption* option) { #if defined(__arm__) // get interval value const char* intervalFile = "/usr/share/dotnet.tizen/lib/crossgen_interval.txt"; std::ifstream inFile(intervalFile); if (inFile) { fprintf(stderr, "crossgen_interval.txt is found\n"); inFile >> __interval; } if (initializePluginManager("normal")) { fprintf(stderr, "Fail to initialize plugin manager\n"); return NI_ERROR_UNKNOWN; } if (initializePathManager(option->runtimeDir, option->tizenFXDir, option->extraDirs)) { fprintf(stderr, "Fail to initialize path manager\n"); return NI_ERROR_UNKNOWN; } __tpa = getTPA(); return NI_ERROR_NONE; #else fprintf(stderr, "crossgen supports arm architecture only. skip ni file generation\n"); return NI_ERROR_NOT_SUPPORTED; #endif } void finalizeNICommon() { __interval = 0; finalizePluginManager(); finalizePathManager(); __tpa.clear(); } void createNiPlatform(bool enableR2R) { const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()}; createNiUnderDirs(platformDirs, 2, enableR2R); } ni_error_e createNiDll(const std::string& dllPath, bool enableR2R) { createCoreLibNI(enableR2R); // System.Private.CoreLib.dll is generated in the createCoreLibNI function. // Skip if input dll is System.Private.CoreLib.dll if (dllPath.find("System.Private.CoreLib.dll") != std::string::npos) { return NI_ERROR_NONE; } return crossgen(dllPath, std::string(), enableR2R); } void createNiUnderDirs(const std::string rootPaths[], int count, bool enableR2R, bool isAppNI) { createCoreLibNI(enableR2R); std::string appPaths; for (int i = 0; i < count; i++) { appPaths += rootPaths[i]; appPaths += ':'; } if (appPaths.back() == ':') appPaths.pop_back(); auto convert = [&appPaths, enableR2R, isAppNI](const std::string& path, const char* name) { if (!crossgen(path, appPaths.c_str(), enableR2R, isAppNI)) { waitInterval(); } }; for (int i = 0; i < count; i++) { scanFilesInDir(rootPaths[i], convert, 1); } } ni_error_e createNiUnderPkgRoot(const std::string& pkgName, bool enableR2R) { std::string pkgRoot; if (getRootPath(pkgName, pkgRoot) != NI_ERROR_NONE) { fprintf(stderr, "Failed to get root path from [%s]\n", pkgName.c_str()); return NI_ERROR_INVALID_PACKAGE; } std::string binDir = concatPath(pkgRoot, "bin"); std::string libDir = concatPath(pkgRoot, "lib"); std::string paths[] = {binDir, libDir}; createNiUnderDirs(paths, 2, enableR2R, true); return NI_ERROR_NONE; } ni_error_e createNiDllUnderPkgRoot(const std::string& pkgName, const std::string& dllPath, bool enableR2R) { std::string pkgRoot; if (getRootPath(pkgName, pkgRoot) < 0) { fprintf(stderr, "Failed to get root path from [%s]\n", pkgName.c_str()); return NI_ERROR_INVALID_PACKAGE; } std::string binDir = concatPath(pkgRoot, "bin"); std::string libDir = concatPath(pkgRoot, "lib"); std::string paths = binDir + ":" + libDir; return crossgen(dllPath, paths, enableR2R, true); } void removeNiPlatform() { std::string coreLib = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll"); std::string coreLibBackup = concatPath(getRuntimeDir(), "System.Private.CoreLib.dll.Backup"); if (!isFileExist(coreLibBackup)) { return; } if (remove(coreLib.c_str())) { fprintf(stderr, "Failed to remove System.Private.CoreLib native image file\n"); } if (rename(coreLibBackup.c_str(), coreLib.c_str())) { fprintf(stderr, "Failed to rename System.Private.CoreLib.Backup to origin\n"); } const std::string platformDirs[] = {getRuntimeDir(), getTizenFXDir()}; removeNiUnderDirs(platformDirs, 2); } void removeNiUnderDirs(const std::string rootPaths[], int count) { auto convert = [](const std::string& path, std::string name) { std::string ni; if (isNativeImage(path)) { if (remove(path.c_str())) { fprintf(stderr, "Failed to remove %s\n", path.c_str()); } } }; for (int i = 0; i < count; i++) scanFilesInDir(rootPaths[i], convert, -1); } ni_error_e removeNiUnderPkgRoot(const std::string& pkgName) { std::string pkgRoot; if (getRootPath(pkgName, pkgRoot) < 0) { fprintf(stderr, "Failed to get root path from [%s]\n", pkgName.c_str()); return NI_ERROR_INVALID_PACKAGE; } std::string binDir = concatPath(pkgRoot, "bin"); std::string libDir = concatPath(pkgRoot, "lib"); std::string paths[] = {binDir, libDir}; removeNiUnderDirs(paths, 2); std::string binNIDir = binDir + APP_NI_SUB_DIR; if (isFileExist(binNIDir)) { if (rmdir(binNIDir.c_str()) != 0) { fprintf(stderr, "Failed to remove app ni dir [%s]\n", binNIDir.c_str()); } } std::string libNIDir = libDir + APP_NI_SUB_DIR; if (isFileExist(libNIDir)) { if (rmdir(libNIDir.c_str()) != 0) { fprintf(stderr, "Failed to remove app ni dir [%s]\n", libNIDir.c_str()); } } return NI_ERROR_NONE; } ni_error_e regenerateAppNI(bool enableR2R) { int ret = 0; pkgmgrinfo_appinfo_metadata_filter_h handle; ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle); if (ret != PMINFO_R_OK) return NI_ERROR_UNKNOWN; ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, AOT_METADATA_KEY, AOT_METADATA_VALUE); if (ret != PMINFO_R_OK) { pkgmgrinfo_appinfo_metadata_filter_destroy(handle); return NI_ERROR_UNKNOWN; } ret = pkgmgrinfo_appinfo_metadata_filter_foreach(handle, appAotCb, &enableR2R); if (ret != PMINFO_R_OK) { fprintf(stderr, "Failed pkgmgrinfo_appinfo_metadata_filter_foreach\n"); pkgmgrinfo_appinfo_metadata_filter_destroy(handle); return NI_ERROR_UNKNOWN; } fprintf(stderr, "Success pkgmgrinfo_appinfo_metadata_filter_foreach\n"); pkgmgrinfo_appinfo_metadata_filter_destroy(handle); return NI_ERROR_NONE; }