Add r2r checker (#355)
[platform/core/dotnet/launcher.git] / NativeLauncher / tool / ni_common.cc
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <pkgmgr-info.h>
18 #include <pkgmgr_installer_info.h>
19 #include <aul.h>
20 #include <tzplatform_config.h>
21
22 #include "log.h"
23 #include "utils.h"
24 #include "pkgmgr_parser_plugin_interface.h"
25
26 #include <wait.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29
30 #include <algorithm>
31 #include <string>
32 #include <fstream>
33 #include <sstream>
34
35 #include <pwd.h>
36 #include <grp.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <sqlite3.h>
40 #include <inttypes.h>
41 #include <errno.h>
42
43 #include "ni_common.h"
44 #include "db_manager.h"
45 #include "tac_common.h"
46 #include "path_manager.h"
47 #include "plugin_manager.h"
48 #include "r2r_checker.h"
49
50 #ifdef  LOG_TAG
51 #undef  LOG_TAG
52 #endif
53 #define LOG_TAG "DOTNET_INSTALLER_PLUGIN"
54
55 #ifndef CROSSGEN_PATH
56 #error "CROSSGEN_PATH is missed"
57 #endif
58
59 #define __XSTR(x) #x
60 #define __STR(x) __XSTR(x)
61 #if defined(__arm__) || defined(__aarch64__)
62 static const char* __NATIVE_LIB_DIR = __STR(NATIVE_LIB_DIR);
63 #endif
64 static const char* __CROSSGEN_PATH = __STR(CROSSGEN_PATH);
65 static const char* __DOTNET_DIR = __STR(DOTNET_DIR);
66 static const char* __READ_ONLY_APP_UPDATE_DIR = __STR(READ_ONLY_APP_UPDATE_DIR);
67
68 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
69 static const char* __SYSTEM_BASE_FILE = __STR(SYSTEM_BASE_FILE);
70 #endif
71
72 #undef __STR
73 #undef __XSTR
74
75 static const char* CORERUN_CMD = "/usr/share/dotnet.tizen/netcoreapp/corerun";
76 static const char* CROSSGEN2_PATH = "/usr/share/dotnet.tizen/netcoreapp/crossgen2/crossgen2.dll";
77 static const char* CLRJIT_PATH = "/usr/share/dotnet.tizen/netcoreapp/libclrjit.so";
78 static const char* CROSSGEN_OPT_JITPATH = "--jitpath";
79 static const char* CROSSGEN_OPT_TARGET_ARCH = "--targetarch";
80 static const char* CROSSGEN_OPT_OUT_NEAR_INPUT = "--out-near-input";
81 static const char* CROSSGEN_OPT_SINGLE_FILE_COMPILATION = "--single-file-compilation";
82 //static const char* CROSSGEN_OPT_PARALLELISM = "--parallelism";
83 //static const char* CROSSGEN_OPT_PARALLELISM_COUNT = "5";
84 static const char* CROSSGEN_OPT_RESILIENT = "--resilient";
85 static const char* CROSSGEN_OPT_OPTIMIZE = "-O";
86 static const char* CROSSGEN_OPT_INPUTBUBBLE = "--inputbubble";
87 static const char* CROSSGEN_OPT_COMPILE_BUBBLE_GENERICS = "--compilebubblegenerics";
88 static const char* CROSSGEN_OPT_VERBOSE = "--verbose";
89 static const char* CROSSGEN_OPT_TUNING = "--tuning";
90 static std::vector<std::string> REF_VECTOR;
91
92 static int __interval = 0;
93 static PathManager* __pm = nullptr;
94
95 static bool isCoreLibPrepared(DWORD flags)
96 {
97         if (flags & NI_FLAGS_ENABLER2R) {
98                 return true;
99         }
100
101         std::string coreLibBackup = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll.Backup");
102         if (isFile(coreLibBackup)) {
103                 return true;
104         } else {
105                 _SERR("The native image of System.Private.CoreLib does not exist\n"
106                                         "Run the command to create the native image\n"
107                                         "# dotnettool --ni-dll /usr/share/dotnet.tizen/netcoreapp/System.Private.CoreLib.dll\n");
108                 return false;
109         }
110 }
111
112 static bool hasCoreLibNI()
113 {
114         std::string ildasm = concatPath(__pm->getRuntimePath(), "ildasm");
115         std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll");
116         std::string cmd = ildasm + " " + coreLib + " -noil -stats | grep '.rsrc'";
117
118         FILE *fp;
119         fp = popen(cmd.c_str(), "r");
120         if (fp != NULL) {
121                 char buff[1024];
122                 if (fgets(buff, sizeof(buff), fp) != NULL) {
123                         pclose(fp);
124                         return false;
125                 }
126                 pclose(fp);
127         }
128         return true;
129 }
130
131 static void waitInterval()
132 {
133         // by the recommand, ignore small value for performance.
134         if (__interval > 10000) {
135                 _SOUT("sleep %d usec", __interval);
136                 usleep(__interval);
137         }
138 }
139
140 static std::string getNIFilePath(const std::string& dllPath)
141 {
142         size_t index = dllPath.find_last_of(".");
143         if (index == std::string::npos) {
144                 _SERR("File doesnot contain extension. fail to get NI file name");
145                 return "";
146         }
147         std::string fName = dllPath.substr(0, index);
148         std::string fExt = dllPath.substr(index, dllPath.length());
149
150         // crossgen generate file with lower case extension only
151         std::transform(fExt.begin(), fExt.end(), fExt.begin(), ::tolower);
152         std::string niPath = fName + ".ni" + fExt;
153
154         return niPath;
155 }
156
157 /**
158  * @brief create the directory including parents directory, and
159  *        copy ownership and smack labels to the created directory.
160  * @param[in] target directory path
161  * @param[in] source directory path to get ownership and smack label
162  * @return if directory created successfully, return true otherwise false
163  */
164 static bool createDirsAndCopyOwnerShip(std::string& target_path, const std::string& source)
165 {
166         struct stat st;
167         mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
168
169         for (std::string::iterator iter = target_path.begin(); iter != target_path.end();) {
170                 std::string::iterator newIter = std::find(iter, target_path.end(), '/');
171                 std::string newPath = std::string(target_path.begin(), newIter);
172
173                 if (!newPath.empty()) {
174                         if (stat(newPath.c_str(), &st) != 0) {
175                                 if (mkdir(newPath.c_str(), mode) != 0 && errno != EEXIST) {
176                                         _SERR("Fail to create app ni directory (%s)", newPath.c_str());
177                                         return false;
178                                 }
179                                 if (!source.empty()) {
180                                         copySmackAndOwnership(source, newPath);
181                                 }
182                         } else {
183                                 if (!S_ISDIR(st.st_mode)) {
184                                         _SERR("Fail. path is not a dir (%s)", newPath.c_str());
185                                         return false;
186                                 }
187                         }
188                 }
189                 iter = newIter;
190                 if(newIter != target_path.end()) {
191                         ++iter;
192                 }
193         }
194
195         return true;
196 }
197
198 static std::string getAppNIFilePath(const std::string& absDllPath, DWORD flags)
199 {
200         std::string niDirPath;
201         std::string prevPath;
202
203         prevPath = getBaseName(absDllPath);
204         niDirPath = concatPath(prevPath, APP_NI_SUB_DIR);
205
206         if (flags & NI_FLAGS_APP_UNDER_RO_AREA) {
207                 niDirPath = replaceAll(niDirPath, getBaseName(__pm->getAppRootPath()), __READ_ONLY_APP_UPDATE_DIR);
208                 _SERR("App is installed in RO area. Change NI path to RW area(%s).", niDirPath.c_str());
209                 _ERR("App is installed in RO area. Change NI path to RW area(%s).", niDirPath.c_str());
210         }
211
212         if (!isDirectory(niDirPath)) {
213                 if (!createDirsAndCopyOwnerShip(niDirPath, prevPath)) {
214                         niDirPath = prevPath;
215                         _SERR("fail to create dir (%s)", niDirPath.c_str());
216                 }
217         }
218
219         return getNIFilePath(concatPath(niDirPath, getFileName(absDllPath)));
220 }
221
222 static bool checkNIExistence(const std::string& absDllPath)
223 {
224         std::string absNIPath = getNIFilePath(absDllPath);
225         if (absNIPath.empty()) {
226                 return false;
227         }
228
229         if (isFile(absNIPath)) {
230                 return true;
231         }
232
233         // native image of System.Private.CoreLib.dll should have to overwrite
234         // original file to support new coreclr
235         if (absDllPath.find("System.Private.CoreLib.dll") != std::string::npos) {
236                 if (hasCoreLibNI()) {
237                         return true;
238                 }
239         }
240
241         return false;
242 }
243
244 static bool checkDllExistInDir(const std::string& path)
245 {
246         bool ret = false;
247         auto func = [&ret](const std::string& f_path, const std::string& f_name) {
248                 if (isManagedAssembly(f_name) || isNativeImage(f_name)) {
249                         ret = true;
250                 }
251         };
252
253         scanFilesInDirectory(path, func, 0);
254
255         return ret;
256 }
257
258 /*
259  * Get the list of managed files in the specific directory
260  * Absolute paths of managed files are stored at the result list.
261  * If native image already exist in the same directory, managed file is ignored.
262  *
263  * Note: System.Private.CoreLib is excluded from the result list.
264  */
265 static ni_error_e getTargetDllList(const std::string& path, std::vector<std::string>& fileList)
266 {
267         if (!isDirectory(path)) {
268                 return NI_ERROR_INVALID_PARAMETER;
269         }
270
271         auto func = [&fileList](const std::string& f_path, const std::string& f_name) {
272                 if (isManagedAssembly(f_path) && !checkNIExistence(f_path)) {
273                         if (f_path.find("System.Private.CoreLib") == std::string::npos) {
274                                 fileList.push_back(getAbsolutePath(f_path));
275                         }
276                 }
277         };
278
279         scanFilesInDirectory(path, func, 0);
280
281         return NI_ERROR_NONE;
282 }
283
284 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
285 static uintptr_t getFileSize(const std::string& path)
286 {
287         struct stat sb;
288
289         if (stat(path.c_str(), &sb) == 0) {
290                 return sb.st_size;
291         }
292
293         return 0;
294 }
295
296 // Get next base address to be used for system ni image from file
297 // __SYSTEM_BASE_FILE should be checked for existance before calling this function
298 static uintptr_t getNextBaseAddrFromFile()
299 {
300         FILE *pFile = fopen(__SYSTEM_BASE_FILE, "r");
301         if (pFile == NULL) {
302                 _SERR("Failed to open %s", __SYSTEM_BASE_FILE);
303                 return 0;
304         }
305
306         uintptr_t addr = 0;
307         uintptr_t size = 0;
308
309         while (fscanf(pFile, "%" SCNxPTR " %" SCNuPTR "", &addr, &size) != EOF) {
310         }
311
312         fclose(pFile);
313
314         return addr + size;
315 }
316
317 // Get next base address to be used for system ni image
318 static uintptr_t getNextBaseAddr()
319 {
320         uintptr_t baseAddr = 0;
321
322         if (!isFile(__SYSTEM_BASE_FILE)) {
323                 // This is the starting address for all default base addresses
324                 baseAddr = DEFAULT_BASE_ADDR_START;
325         } else {
326                 baseAddr = getNextBaseAddrFromFile();
327
328                 // Round to a multple of 64K (see ZapImage::CalculateZapBaseAddress in CoreCLR)
329                 uintptr_t BASE_ADDRESS_ALIGNMENT = 0xffff;
330                 baseAddr = (baseAddr + BASE_ADDRESS_ALIGNMENT) & ~BASE_ADDRESS_ALIGNMENT;
331         }
332
333         return baseAddr;
334 }
335
336 // Save base address of system ni image to file
337 static void updateBaseAddrFile(const std::string& absNIPath, uintptr_t baseAddr)
338 {
339         uintptr_t niSize = getFileSize(absNIPath);
340         if (niSize == 0) {
341                 _SERR("File %s doesn't exist", absNIPath.c_str());
342                 return;
343         }
344
345         // Write new entry to the file
346         FILE *pFile = fopen(__SYSTEM_BASE_FILE, "a");
347         if (pFile == NULL) {
348                 _SERR("Failed to open %s", __SYSTEM_BASE_FILE);
349                 return;
350         }
351
352         fprintf(pFile, "%" PRIxPTR " %" PRIuPTR "\n", baseAddr, niSize);
353         fclose(pFile);
354 }
355
356 // check if dll is listed in TPA
357 static bool isTPADll(const std::string& dllPath)
358 {
359         std::string absPath = getBaseName(getAbsolutePath(dllPath));
360
361         std::vector<std::string> paths = __pm->getPlatformAssembliesPaths();
362         for (unsigned int i = 0; i < paths.size(); i++) {
363                 if (paths[i].find(getBaseName(absPath)) != std::string::npos) {
364                         return true;
365                 }
366         }
367
368         return false;
369 }
370 #endif
371
372 // baseAddr should be checked in file before getting here
373 static ni_error_e crossgen(const std::string& dllPath, const std::string& appPath, DWORD flags)
374 {
375         std::string absDllPath = getAbsolutePath(dllPath);
376         std::string absNIPath;
377
378         bool isAppNI = flags & NI_FLAGS_APPNI;
379         if (isAppNI && strstr(absDllPath.c_str(), __DOTNET_DIR) == NULL) {
380                 absNIPath = getAppNIFilePath(absDllPath, flags);
381                 if (exist(absNIPath)) {
382                         return NI_ERROR_ALREADY_EXIST;
383                 }
384         } else {
385                 absNIPath = getNIFilePath(absDllPath);
386                 if (checkNIExistence(absDllPath)) {
387                         return NI_ERROR_ALREADY_EXIST;
388                 }
389         }
390
391         if (absNIPath.empty()) {
392                 _SERR("Fail to get ni file name");
393                 return NI_ERROR_UNKNOWN;
394         }
395
396 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
397         uintptr_t baseAddr = 0;
398
399         if (isTPADll(dllPath)) {
400                 baseAddr = getNextBaseAddr();
401         }
402 #endif
403
404         pid_t pid = fork();
405         if (pid == -1)
406                 return NI_ERROR_UNKNOWN;
407
408         if (pid > 0) {
409                 int status;
410                 waitpid(pid, &status, 0);
411                 if (WIFEXITED(status)) {
412                         // Do not use checkNIExistence() function to check whether ni file created or not.
413                         // checkNIExistence() return false for System.Private.Corelib.dll
414                         if (isFile(absNIPath)) {
415                                 copySmackAndOwnership(absDllPath, absNIPath);
416                                 std::string absPdbPath = replaceAll(absDllPath, ".dll", ".pdb");
417                                 std::string pdbFilePath = replaceAll(absNIPath, ".ni.dll", ".pdb");
418                                 if (isFile(absPdbPath) && (absPdbPath != pdbFilePath)) {
419                                         if (!copyFile(absPdbPath, pdbFilePath)) {
420                                                 _SERR("Failed to copy a .pdb file");
421                                         } else {
422                                                 copySmackAndOwnership(absPdbPath, pdbFilePath);
423                                         }
424                                 }
425 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
426                                 if (baseAddr != 0) {
427                                         updateBaseAddrFile(absNIPath, baseAddr);
428                                 }
429 #endif
430                                 return NI_ERROR_NONE;
431                         } else {
432                                 _SERR("Fail to create native image for %s", dllPath.c_str());
433                                 return NI_ERROR_NO_SUCH_FILE;
434                         }
435                 }
436         } else {
437                 std::string jitPath = __pm->getRuntimePath() + "/libclrjit.so";
438                 std::vector<const char*> argv = {
439                         __CROSSGEN_PATH,
440                         "/nologo",
441                         "/JITPath", jitPath.c_str()
442                 };
443
444                 bool compat = flags & NI_FLAGS_COMPATIBILITY;
445                 argv.push_back(compat ? "/Platform_Assemblies_Pathes" : "/p");
446                 std::vector<std::string> paths = __pm->getPlatformAssembliesPaths();
447                 std::string platformAssembliesPaths;
448                 for (const auto &path : paths) {
449                         if (!platformAssembliesPaths.empty()) {
450                                 platformAssembliesPaths += ":";
451                         }
452                         platformAssembliesPaths += path;
453                 }
454                 argv.push_back(platformAssembliesPaths.c_str());
455
456                 bool enableR2R = flags & NI_FLAGS_ENABLER2R;
457                 if (!enableR2R) {
458                         argv.push_back("/FragileNonVersionable");
459                 }
460
461                 if (flags & NI_FLAGS_VERBOSE) {
462                         argv.push_back("/verbose");
463                 }
464
465                 if (flags & NI_FLAGS_INSTRUMENT) {
466                         argv.push_back("/Tuning");
467                 }
468
469 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
470                 std::string baseAddrString;
471                 if (baseAddr != 0) {
472                         argv.push_back("/BaseAddress");
473                         std::stringstream ss;
474                         ss << "0x" << std::hex << baseAddr;
475                         baseAddrString = ss.str();
476                         argv.push_back(baseAddrString.c_str());
477                 }
478 #endif
479
480                 argv.push_back("/App_Paths");
481                 std::string absAppPath;
482                 if (!appPath.empty()) {
483                         absAppPath = appPath;
484                 } else {
485                         absAppPath = getBaseName(absDllPath);
486                 }
487                 argv.push_back(absAppPath.c_str());
488
489                 argv.push_back("/out");
490                 argv.push_back(absNIPath.c_str());
491
492                 argv.push_back(absDllPath.c_str());
493                 argv.push_back(nullptr);
494
495                 _SOUT("+ %s (%s)", absDllPath.c_str(), enableR2R ? "R2R" : "FNV");
496
497                 execv(__CROSSGEN_PATH, const_cast<char* const*>(argv.data()));
498                 exit(0);
499         }
500
501         return NI_ERROR_NONE;
502 }
503
504 static void makeArgs(std::vector<const char*>& args, const std::vector<std::string>& refPaths, DWORD flags)
505 {
506         args.push_back(CORERUN_CMD);
507         args.push_back(CROSSGEN2_PATH);
508         args.push_back(CROSSGEN_OPT_JITPATH);
509         args.push_back(CLRJIT_PATH);
510         args.push_back(CROSSGEN_OPT_TARGET_ARCH);
511         args.push_back(ARCHITECTURE_IDENTIFIER);
512         if (!(flags & NI_FLAGS_NO_PIPELINE)) {
513                 args.push_back(CROSSGEN_OPT_OUT_NEAR_INPUT);
514                 args.push_back(CROSSGEN_OPT_SINGLE_FILE_COMPILATION);
515         }
516         //args.push_back(OPT_PARALLELISM);
517         //args.push_back(OPT_PARALLELISM_COUNT);
518         args.push_back(CROSSGEN_OPT_RESILIENT);
519
520         args.push_back(CROSSGEN_OPT_OPTIMIZE);
521
522         if (flags & NI_FLAGS_INPUT_BUBBLE) {
523                 args.push_back(CROSSGEN_OPT_INPUTBUBBLE);
524                 args.push_back(CROSSGEN_OPT_COMPILE_BUBBLE_GENERICS);
525         }
526
527         if (flags & NI_FLAGS_VERBOSE) {
528                 args.push_back(CROSSGEN_OPT_VERBOSE);
529         }
530
531         if (flags & NI_FLAGS_INSTRUMENT) {
532                 args.push_back(CROSSGEN_OPT_TUNING);
533         }
534
535         REF_VECTOR.clear();
536
537         // set reference path
538         std::vector<std::string> paths = __pm->getPlatformAssembliesPaths();
539         for (const auto &path : paths) {
540                 REF_VECTOR.push_back("-r:" + path + "/*.dll");
541         }
542
543         for (const auto &path : refPaths) {
544                 if (checkDllExistInDir(path)) {
545                         REF_VECTOR.push_back("-r:" + path + "/*.dll");
546                 }
547         }
548
549         for (const auto &path : REF_VECTOR) {
550                 args.push_back(path.c_str());
551         }
552 }
553
554 static void clearArgs(std::vector<const char*>& args)
555 {
556         REF_VECTOR.clear();
557         args.clear();
558 }
559
560 static ni_error_e makePdbSymlinkForNI(std::string dllPath, std::string niPath)
561 {
562         std::string pdbPath = changeExtension(dllPath, ".dll", ".pdb");
563         try {
564                 if (exist(pdbPath)) {
565                         std::string targetPDBPath = changeExtension(niPath, ".ni.dll", ".pdb");
566                         if (!exist(targetPDBPath)) {
567                                 bf::create_symlink(pdbPath, targetPDBPath);
568                                 copySmackAndOwnership(pdbPath, targetPDBPath, true);
569                         }
570                 }
571         } catch (const bf::filesystem_error& error) {
572                 _SERR("Fail to create symlink for %s", pdbPath.c_str());
573                 return NI_ERROR_UNKNOWN;
574         }
575
576         return NI_ERROR_NONE;
577 }
578
579 static ni_error_e crossgen2PipeLine(const std::vector<std::string>& dllList, const std::vector<std::string>& refPaths, DWORD flags)
580 {
581         // fork crossgen2
582         pid_t pid = fork();
583         if (pid == -1)
584                 return NI_ERROR_UNKNOWN;
585
586         if (pid > 0) {
587                 int status;
588                 waitpid(pid, &status, 0);
589                 if (WIFEXITED(status)) {
590                         for (auto& dllPath: dllList) {
591                                 std::string niPath = changeExtension(dllPath, ".dll", ".ni.dll");
592
593                                 if (!exist(niPath)) {
594                                         _SERR("Fail to create native image for %s", dllPath.c_str());
595                                         return NI_ERROR_NO_SUCH_FILE;
596                                 }
597
598                                 copySmackAndOwnership(dllPath, niPath);
599                                 // if AppNI then move ni.dll file to .native_image and copy pdb to .native_image
600                                 if (flags & NI_FLAGS_APPNI) {
601                                         std::string appNIPath = getAppNIFilePath(dllPath, flags);
602                                         moveFile(niPath, appNIPath);
603                                         makePdbSymlinkForNI(dllPath, appNIPath);
604                                         niPath = appNIPath;
605                                 }
606                                 _SOUT("Native image %s generated successfully.", niPath.c_str());
607                         }
608                 }
609         } else {
610                 std::vector<const char*> argv;
611
612                 makeArgs(argv, refPaths, flags);
613
614                 // add input files at the end of parameter
615                 for (const auto &input : dllList) {
616                         argv.push_back(input.c_str());
617                         _SOUT("+ %s", input.c_str());
618                 }
619
620                 argv.push_back(nullptr);
621
622                 // print cmd
623                 //for (auto &arg: argv) _SOUT("+ %s", arg);
624
625                 execv(CORERUN_CMD, const_cast<char* const*>(argv.data()));
626
627                 clearArgs(argv);
628                 exit(0);
629         }
630
631         return NI_ERROR_NONE;
632 }
633
634 static ni_error_e crossgen2NoPipeLine(const std::vector<std::string>& dllList, const std::vector<std::string>& refPaths, DWORD flags)
635 {
636         for (auto& dllPath : dllList) {
637                 std::string niPath;
638                 if (flags & NI_FLAGS_APPNI) {
639                         niPath = getAppNIFilePath(dllPath, flags);
640                 } else {
641                         niPath = getNIFilePath(dllPath);
642                 }
643
644                 // fork crossgen2
645                 pid_t pid = fork();
646                 if (pid == -1)
647                         return NI_ERROR_UNKNOWN;
648
649                 if (pid > 0) {
650                         int status;
651                         waitpid(pid, &status, 0);
652                         if (WIFEXITED(status)) {
653                                 if (!exist(niPath)) {
654                                         _SERR("Fail to create native image for %s", dllPath.c_str());
655                                         return NI_ERROR_NO_SUCH_FILE;
656                                 }
657
658                                 copySmackAndOwnership(dllPath, niPath);
659                                 if (flags & NI_FLAGS_APPNI) {
660                                         makePdbSymlinkForNI(dllPath, niPath);
661                                 }
662                                 _SOUT("Native image %s generated successfully.", niPath.c_str());
663                         }
664                 } else {
665                         std::vector<const char*> argv;
666                         makeArgs(argv, refPaths, flags);
667
668                         argv.push_back("-o");
669                         argv.push_back(niPath.c_str());
670
671                         argv.push_back(dllPath.c_str());
672                         _SOUT("+ %s", dllPath.c_str());
673
674                         // end param
675                         argv.push_back(nullptr);
676
677                         // print cmd
678                         //for (auto &arg: argv) _SOUT("+ %s", arg);
679
680                         execv(CORERUN_CMD, const_cast<char* const*>(argv.data()));
681
682                         clearArgs(argv);
683                         exit(0);
684                 }
685
686                 waitInterval();
687         }
688
689         return NI_ERROR_NONE;
690 }
691
692
693 static ni_error_e doAOTList(std::vector<std::string>& dllList, const std::string& refPaths, DWORD flags)
694 {
695         if (dllList.empty()) {
696                 return NI_ERROR_INVALID_PARAMETER;
697         }
698         // When performing AOT for one Dll, an error is returned when an error occurs.
699         // However, when processing multiple dlls at once, only the log for errors is output and skipped.
700
701         for (auto it = dllList.begin(); it != dllList.end(); it++) {
702                 std::string f = *it;
703                 if (!isFile(f)) {
704                         _SERR("dll file is not exist : %s", f.c_str());
705                         dllList.erase(it--);
706                 }
707                 if (!isManagedAssembly(f)) {
708                         _SERR("Input file is not a dll file : %s", f.c_str());
709                         dllList.erase(it--);
710                 }
711         }
712
713         if (!isFile(concatPath(__pm->getRuntimePath(), "crossgen"))) {
714                 _SERR("crossgen1 doesnot supported. use crossgen2 forcibily");
715                 flags |= NI_FLAGS_USE_CROSSGEN2;
716         }
717
718         if (flags & NI_FLAGS_USE_CROSSGEN2) {           // crossgen2
719                 std::vector<std::string> paths;
720                 splitPath(refPaths, paths);
721
722                 if (flags & NI_FLAGS_NO_PIPELINE) {
723                         crossgen2NoPipeLine(dllList, paths, flags);
724                 } else {
725                         crossgen2PipeLine(dllList, paths, flags);
726                 }
727         } else {                                                                        // crossgen1
728                 for (auto& dll : dllList) {
729                         crossgen(dll, refPaths, flags);
730                         waitInterval();
731                 }
732         }
733
734         return NI_ERROR_NONE;
735 }
736
737 static ni_error_e doAOTFile(const std::string& dllFile, const std::string& refPaths, DWORD flags)
738 {
739         if (!isFile(dllFile)) {
740                 _SERR("dll file is not exist : %s", dllFile.c_str());
741                 return NI_ERROR_NO_SUCH_FILE;
742         }
743
744         if (!isManagedAssembly(dllFile)) {
745                 _SERR("Failed. Input parameter is not managed dll (%s)\n", dllFile.c_str());
746                 return NI_ERROR_INVALID_PARAMETER;
747         }
748
749         std::vector<std::string> dllList;
750         dllList.push_back(getAbsolutePath(dllFile));
751         return doAOTList(dllList, refPaths, flags);
752 }
753
754 // callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach"
755 static int appAotCb(pkgmgrinfo_appinfo_h handle, void *userData)
756 {
757         char *pkgId = NULL;
758         int ret = 0;
759         DWORD *pFlags = (DWORD*)userData;
760
761         ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
762         if (ret != PMINFO_R_OK) {
763                 _SERR("Failed to get pkgid");
764                 return -1;
765         }
766
767         if (removeNIUnderPkgRoot(pkgId) != NI_ERROR_NONE) {
768                 _SERR("Failed to remove previous dlls from [%s]", pkgId);
769                 return -1;
770         }
771
772         if (createNIUnderPkgRoot(pkgId, *pFlags) != NI_ERROR_NONE) {
773                 _SERR("Failed to generate NI file [%s]", pkgId);
774                 return -1;
775         } else {
776                 _SOUT("Complete make application to native image");
777         }
778
779         return 0;
780 }
781
782 static ni_error_e createCoreLibNI(DWORD flags)
783 {
784         std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll");
785         std::string niCoreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.ni.dll");
786         std::string coreLibBackup = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll.Backup");
787
788         if (!isFile(coreLibBackup) && !hasCoreLibNI()) {
789                 if (doAOTFile(coreLib, std::string(), flags) == NI_ERROR_NONE) {
790                         if (rename(coreLib.c_str(), coreLibBackup.c_str())) {
791                                 _SERR("Failed to rename System.Private.CoreLib.dll");
792                                 return NI_ERROR_CORE_NI_FILE;
793                         }
794                         if (rename(niCoreLib.c_str(), coreLib.c_str())) {
795                                 _SERR("Failed to rename System.Private.CoreLib.ni.dll");
796                                 return NI_ERROR_CORE_NI_FILE;
797                         }
798                 } else {
799                         _SERR("Failed to create native image for %s", coreLib.c_str());
800                         return NI_ERROR_CORE_NI_FILE;
801                 }
802         }
803         return NI_ERROR_NONE;
804 }
805
806 ni_error_e initNICommon()
807 {
808 #if defined(__arm__) || defined(__aarch64__)
809         // get interval value
810         const static std::string intervalFile = concatPath(__NATIVE_LIB_DIR, "crossgen_interval.txt");
811         std::ifstream inFile(intervalFile);
812         if (inFile) {
813                 _SOUT("crossgen_interval.txt is found");
814                 inFile >> __interval;
815         }
816
817         if (initializePluginManager("normal")) {
818                 _SERR("Fail to initialize PluginManager");
819                 return NI_ERROR_UNKNOWN;
820         }
821
822         try {
823                 __pm = new PathManager();
824         } catch (const std::exception& e) {
825                 _SERR("Failed to create PathManager");
826                 return NI_ERROR_UNKNOWN;
827         }
828
829         char* pluginDllPaths = pluginGetDllPath();
830         if (pluginDllPaths && pluginDllPaths[0] != '\0') {
831                 __pm->addPlatformAssembliesPaths(pluginDllPaths, true);
832         }
833
834         char* pluginNativePaths = pluginGetNativeDllSearchingPath();
835         if (pluginNativePaths && pluginNativePaths[0] != '\0') {
836                 __pm->addNativeDllSearchingPaths(pluginNativePaths, true);
837         }
838
839         return NI_ERROR_NONE;
840 #else
841         _SERR("crossgen supports arm/arm64 architecture only. skip ni file generation");
842         return NI_ERROR_NOT_SUPPORTED;
843 #endif
844 }
845
846 void finalizeNICommon()
847 {
848         __interval = 0;
849
850         finalizePluginManager();
851
852         delete(__pm);
853         __pm = nullptr;
854 }
855
856 ni_error_e createNIPlatform(DWORD flags)
857 {
858         ni_error_e ret = createCoreLibNI(flags);
859         if (ret != NI_ERROR_NONE) {
860                 return ret;
861         }
862
863         return createNIUnderDirs(__pm->getRuntimePath() + ":" + __pm->getTizenFXPath(), flags);
864 }
865
866 ni_error_e createNIDll(const std::string& dllPath, DWORD flags)
867 {
868         if (dllPath.find("System.Private.CoreLib.dll") != std::string::npos) {
869                 return createCoreLibNI(flags);
870         }
871
872         if (!isCoreLibPrepared(flags)) {
873                 return NI_ERROR_CORE_NI_FILE;
874         }
875
876         return doAOTFile(dllPath, std::string(), flags);
877 }
878
879 ni_error_e createNIUnderTAC(const std::string& targetPath, const std::string& refPaths, DWORD flags)
880 {
881         ni_error_e ret;
882
883         // get managed file list from targetPath
884         std::vector<std::string> dllList;
885         ret = getTargetDllList(targetPath, dllList);
886         if (ret != NI_ERROR_NONE) {
887                 return ret;
888         }
889
890         std::vector<std::string> needNIList;
891         std::vector<std::string> niList;
892
893         for (auto &dll : dllList) {
894                 if (!checkNIExistence(dll)) {
895                         needNIList.push_back(dll);
896                 }
897                 niList.push_back(getNIFilePath(dll));
898         }
899
900         if (!needNIList.empty()) {
901                 // NI fils of TAC-related dlls under /opt/usr/dotnet should not be created under .native_image directory.
902                 // So, unset NI_FLAGS_APPNI temporally and restore it after running AOT.
903                 flags &= ~NI_FLAGS_APPNI;
904                 ret = doAOTList(needNIList, refPaths, flags);
905                 flags |= NI_FLAGS_APPNI;
906                 if (ret != NI_ERROR_NONE) {
907                         return ret;
908                 }
909         }
910
911         for (auto &niPath : niList) {
912                 if (exist(niPath)) {
913                         std::string symNIPath = concatPath(targetPath, getFileName(niPath));
914                         if (!exist(symNIPath)) {
915                                 bf::create_symlink(niPath, symNIPath);
916                                 copySmackAndOwnership(targetPath.c_str(), symNIPath.c_str(), true);
917                                 _SOUT("%s symbolic link file generated successfully.", symNIPath.c_str());
918                                 _INFO("%s symbolic link file generated successfully.", symNIPath.c_str());
919                         }
920                 }
921         }
922
923         return NI_ERROR_NONE;
924 }
925
926
927 ni_error_e createNIUnderDirs(const std::string& rootPaths, DWORD flags)
928 {
929         ni_error_e ret = NI_ERROR_NONE;
930         if (!isCoreLibPrepared(flags)) {
931                 return NI_ERROR_CORE_NI_FILE;
932         }
933
934         std::vector<std::string> fileList;
935         std::vector<std::string> paths;
936         splitPath(rootPaths, paths);
937
938         for (const auto &path : paths) {
939                 if (!exist(path)) {
940                         continue;
941                 }
942
943                 if (path.find(TAC_SYMLINK_SUB_DIR) != std::string::npos) {
944                         ret = createNIUnderTAC(path, rootPaths, flags);
945                         if (ret != NI_ERROR_NONE) {
946                                 return ret;
947                         }
948                 } else {
949                         ret = getTargetDllList(path, fileList);
950                         if (ret != NI_ERROR_NONE) {
951                                 return ret;
952                         }
953                 }
954         }
955
956         if (fileList.empty()) {
957                 return NI_ERROR_NONE;
958         }
959
960         return doAOTList(fileList, rootPaths, flags);
961 }
962
963 ni_error_e createNIUnderPkgRoot(const std::string& pkgId, DWORD flags)
964 {
965         std::string rootPath = getRootPath(pkgId);
966         if (rootPath.empty()) {
967                 _SERR("Failed to get root path from [%s]", pkgId.c_str());
968                 return NI_ERROR_INVALID_PACKAGE;
969         }
970
971         __pm->setAppRootPath(rootPath);
972
973         flags |= NI_FLAGS_APPNI;
974
975         if (isReadOnlyArea(rootPath)) {
976                 flags |= NI_FLAGS_APP_UNDER_RO_AREA;
977                 flags |= NI_FLAGS_NO_PIPELINE;
978                 _SERR("Only no-pipeline mode supported for RO app. Set no-pipeline option forcibly");
979         } else {
980                 flags &= ~NI_FLAGS_APP_UNDER_RO_AREA;
981         }
982
983         // create native image under bin and lib directory
984         // tac directory is skipped in the createNIUnderDirs.
985         return createNIUnderDirs(__pm->getAppPaths(), flags);
986 }
987
988 void removeNIPlatform()
989 {
990         std::string coreLib = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll");
991         std::string coreLibBackup = concatPath(__pm->getRuntimePath(), "System.Private.CoreLib.dll.Backup");
992
993         if (hasCoreLibNI()) {
994                 if (!isFile(coreLibBackup)) {
995                         return;
996                 }
997
998                 if (remove(coreLib.c_str())) {
999                         _SERR("Failed to remove System.Private.CoreLib native image file");
1000                 }
1001                 if (rename(coreLibBackup.c_str(), coreLib.c_str())) {
1002                         _SERR("Failed to rename System.Private.CoreLib.Backup to origin");
1003                 }
1004         }
1005
1006 #ifdef UNIQUE_DEFAULT_BASE_ADDR_SUPPORT
1007         if (isFile(__SYSTEM_BASE_FILE)) {
1008                 if (remove(__SYSTEM_BASE_FILE)) {
1009                         _SERR("Failed to remove %s", __SYSTEM_BASE_FILE);
1010                 }
1011         }
1012 #endif
1013
1014         removeNIUnderDirs(__pm->getRuntimePath() + ":" + __pm->getTizenFXPath());
1015 }
1016
1017 void removeNIUnderDirs(const std::string& rootPaths)
1018 {
1019         auto convert = [](const std::string& path, const std::string& filename) {
1020                 if (isNativeImage(path)) {
1021                         if (remove(path.c_str())) {
1022                                 _SERR("Failed to remove %s", path.c_str());
1023                         }
1024                 }
1025         };
1026
1027         std::vector<std::string> paths;
1028         splitPath(rootPaths, paths);
1029         for (const auto &path : paths) {
1030                 scanFilesInDirectory(path, convert, -1);
1031         }
1032 }
1033
1034 ni_error_e removeNIUnderPkgRoot(const std::string& pkgId)
1035 {
1036         std::string rootPath = getRootPath(pkgId);
1037         if (rootPath.empty()) {
1038                 _SERR("Failed to get root path from [%s]", pkgId.c_str());
1039                 return NI_ERROR_INVALID_PACKAGE;
1040         }
1041
1042         __pm->setAppRootPath(rootPath);
1043
1044         // getAppNIPaths returns bin/.native_image, lib/.native_image and .tac_symlink.
1045         std::string appNIPaths = __pm->getAppNIPaths();
1046         std::vector<std::string> paths;
1047         splitPath(appNIPaths, paths);
1048         for (const auto &path : paths) {
1049                 if (!isReadOnlyArea(path)) {
1050                         // Only the native image inside the TAC should be removed.
1051                         if (strstr(path.c_str(), TAC_SYMLINK_SUB_DIR) != NULL) {
1052                                 removeNIUnderDirs(path);
1053                         } else {
1054                                 if (isDirectory(path)) {
1055                                         if (!removeAll(path.c_str())) {
1056                                                 _SERR("Failed to remove app ni dir [%s]", path.c_str());
1057                                         }
1058                                 }
1059                         }
1060                 }
1061         }
1062
1063         // In special cases, the ni file may exist in the dll location.
1064         // The code below is to avoid this exceptional case.
1065         std::string appPaths = __pm->getAppPaths();
1066         splitPath(appPaths, paths);
1067         for (const auto &path : paths) {
1068                 if (isDirectory(path)) {
1069                         removeNIUnderDirs(path);
1070                 }
1071         }
1072
1073         return NI_ERROR_NONE;
1074 }
1075
1076 ni_error_e regenerateAppNI(DWORD flags)
1077 {
1078         if (!isCoreLibPrepared(flags)) {
1079                 return NI_ERROR_CORE_NI_FILE;
1080         }
1081
1082         int ret = 0;
1083         pkgmgrinfo_appinfo_metadata_filter_h handle;
1084
1085         ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
1086         if (ret != PMINFO_R_OK)
1087                 return NI_ERROR_UNKNOWN;
1088
1089         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, AOT_METADATA_KEY, METADATA_VALUE);
1090         if (ret != PMINFO_R_OK) {
1091                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1092                 return NI_ERROR_UNKNOWN;
1093         }
1094
1095         ret = pkgmgrMDFilterForeach(handle, appAotCb, &flags);
1096         if (ret != 0) {
1097                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1098                 return NI_ERROR_UNKNOWN;
1099         }
1100
1101         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1102         return NI_ERROR_NONE;
1103 }
1104
1105 // callback function of "pkgmgrinfo_appinfo_metadata_filter_foreach"
1106 static int regenTacCb(pkgmgrinfo_appinfo_h handle, void *userData)
1107 {
1108         char *pkgId = NULL;
1109         DWORD *pFlags = (DWORD*)userData;
1110
1111         int ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
1112         if (ret != PMINFO_R_OK || pkgId == NULL) {
1113                 _SERR("Failed to get pkgid");
1114                 return -1;
1115         }
1116
1117         sqlite3 *tac_db = openDB(TAC_APP_LIST_DB);
1118         if (!tac_db) {
1119                 _SERR("Sqlite open error");
1120                 return -1;
1121         }
1122         sqlite3_exec(tac_db, "BEGIN;", NULL, NULL, NULL);
1123
1124         char *sql = sqlite3_mprintf("SELECT * FROM TAC WHERE PKGID = %Q;", pkgId);
1125         std::vector<std::string> nugets = selectDB(tac_db, sql);
1126         sqlite3_free(sql);
1127
1128         if (tac_db) {
1129                 closeDB(tac_db);
1130                 tac_db = NULL;
1131         }
1132
1133         std::string nugetPaths;
1134         for (const auto &nuget : nugets) {
1135                 if (!nugetPaths.empty()) {
1136                         nugetPaths += ":";
1137                 }
1138                 nugetPaths += concatPath(__DOTNET_DIR, nuget);
1139         }
1140
1141         for (auto& nuget : nugets) {
1142                 createNIUnderTAC(concatPath(__DOTNET_DIR, nuget), nugetPaths, *pFlags);
1143         }
1144
1145         return 0;
1146 }
1147
1148 ni_error_e regenerateTACNI(DWORD flags)
1149 {
1150         if (!isCoreLibPrepared(flags)) {
1151                 return NI_ERROR_CORE_NI_FILE;
1152         }
1153
1154         removeNIUnderDirs(__DOTNET_DIR);
1155
1156         pkgmgrinfo_appinfo_metadata_filter_h handle;
1157         int ret = pkgmgrinfo_appinfo_metadata_filter_create(&handle);
1158         if (ret != PMINFO_R_OK) {
1159                 return NI_ERROR_UNKNOWN;
1160         }
1161
1162         ret = pkgmgrinfo_appinfo_metadata_filter_add(handle, TAC_METADATA_KEY, METADATA_VALUE);
1163         if (ret != PMINFO_R_OK) {
1164                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1165                 return NI_ERROR_UNKNOWN;
1166         }
1167
1168         ret = pkgmgrMDFilterForeach(handle, regenTacCb, &flags);
1169         if (ret != 0) {
1170                 pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1171                 return NI_ERROR_UNKNOWN;
1172         }
1173
1174         pkgmgrinfo_appinfo_metadata_filter_destroy(handle);
1175
1176         return NI_ERROR_NONE;
1177 }
1178
1179 static std::vector<uid_t> getUserIds()
1180 {
1181         std::vector<uid_t> list;
1182
1183         while (true) {
1184                 errno = 0; // so we can distinguish errors from no more entries
1185                 passwd* entry = getpwent();
1186                 if (!entry) {
1187                         if (errno) {
1188                                 _SERR("Error while getting userIDs");
1189                                 list.clear();
1190                                 return list;
1191                         }
1192                         break;
1193                 }
1194                 list.push_back(entry->pw_uid);
1195         }
1196         endpwent();
1197
1198         return list;
1199 }
1200
1201 static std::string getAppDataPath(const std::string& pkgId, uid_t uid)
1202 {
1203         std::string pDataFile;
1204
1205         tzplatform_set_user(uid);
1206
1207         const char* tzUserApp = tzplatform_getenv(TZ_USER_APP);
1208         if (tzUserApp != NULL) {
1209                 pDataFile = std::string(tzUserApp) + "/" + pkgId + "/data/";
1210         }
1211
1212         tzplatform_reset_user();
1213
1214         return pDataFile;
1215 }
1216
1217 ni_error_e removeAppProfileData(const std::string& pkgId)
1218 {
1219         if (pkgId.empty()) {
1220                 return NI_ERROR_INVALID_PARAMETER;
1221         }
1222
1223         std::vector<uid_t> uidList = getUserIds();
1224         for (auto& uid : uidList) {
1225                 // get data path from pkgid
1226                 std::string dataPath = getAppDataPath(pkgId, uid);
1227                 if (!dataPath.empty() && exist(dataPath)) {
1228                         std::string pDataFile = dataPath + PROFILE_BASENAME;
1229
1230                         if (exist(pDataFile)) {
1231                                 if (!removeFile(pDataFile)) {
1232                                         _SERR("Fail to remove profile data file (%s).", pDataFile.c_str());
1233                                         return NI_ERROR_UNKNOWN;
1234                                 }
1235                                 _SOUT("Profile data (%s) is removed successfully", pDataFile.c_str());
1236                         }
1237                 }
1238         }
1239
1240         return NI_ERROR_NONE;
1241 }
1242
1243 static int appTypeListCb(pkgmgrinfo_appinfo_h handle, void *user_data)
1244 {
1245         char *pkgId = NULL;
1246         int ret = pkgmgrinfo_appinfo_get_pkgid(handle, &pkgId);
1247         if (ret != PMINFO_R_OK || pkgId == NULL) {
1248                 _SERR("Fail to get pkgid");
1249                 return 0;
1250         }
1251
1252         if (removeAppProfileData(pkgId) != NI_ERROR_NONE) {
1253                 _SERR("Fail to remove profile data for (%s)", pkgId);
1254         }
1255
1256         return 0;
1257 }
1258
1259 static ni_error_e removeAppProfileByAppType(const char* type)
1260 {
1261         int ret;
1262
1263         pkgmgrinfo_appinfo_filter_h filter;
1264
1265         ret = pkgmgrinfo_appinfo_filter_create(&filter);
1266         if (ret != PMINFO_R_OK) {
1267                 _SERR("Fail to create appinfo filter");
1268                 return NI_ERROR_UNKNOWN;
1269         }
1270
1271         ret = pkgmgrinfo_appinfo_filter_add_string(filter, PMINFO_APPINFO_PROP_APP_TYPE, type);
1272         if (ret != PMINFO_R_OK) {
1273                 pkgmgrinfo_appinfo_filter_destroy(filter);
1274                 _SERR("Fail to add appinfo filter (%s)", type);
1275                 return NI_ERROR_UNKNOWN;
1276         }
1277
1278         ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(filter, appTypeListCb, NULL);
1279         if (ret != PMINFO_R_OK) {
1280                 _SERR("Fail to pkgmgrinfo_pkginfo_filter_foreach_pkginfo");
1281                 pkgmgrinfo_appinfo_filter_destroy(filter);
1282                 return NI_ERROR_UNKNOWN;
1283         }
1284
1285         pkgmgrinfo_appinfo_filter_destroy(filter);
1286
1287         return NI_ERROR_NONE;
1288 }
1289
1290 void removeAllAppProfileData()
1291 {
1292         std::vector<const char*> appTypeList = {"dotnet", "dotnet-nui", "dotnet-inhouse"};
1293
1294         for (auto& type : appTypeList) {
1295                 if (removeAppProfileByAppType(type) != NI_ERROR_NONE) {
1296                         _SERR("Fail to removeAppProfileByAppType for type (%s)", type);
1297                 }
1298         }
1299 }