1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "cj_environment.h"
17 
18 #include <string>
19 
20 #include "cj_hilog.h"
21 #include "cj_invoker.h"
22 #ifdef __OHOS__
23 #include <dlfcn.h>
24 #endif
25 #include "dynamic_loader.h"
26 #ifdef WITH_EVENT_HANDLER
27 #include "event_handler.h"
28 #endif
29 
30 namespace OHOS {
31 namespace {
32 const char DEBUGGER_LIBNAME[] = "libcj_debugger.z.so";
33 const char DEBUGGER_SYMBOL_NAME[] = "StartDebuggerServer";
34 const char INIT_CJRUNTIME_SYMBOL_NAME[] = "InitCJRuntime";
35 const char INIT_UISCHEDULER_SYMBOL_NAME[] = "InitUIScheduler";
36 const char RUN_UISCHEDULER_SYMBOL_NAME[] = "RunUIScheduler";
37 const char FINI_CJRUNTIME_SYMBOL_NAME[] = "FiniCJRuntime";
38 const char INIT_CJLIBRARY_SYMBOL_NAME[] = "InitCJLibrary";
39 const char REGISTER_EVENTHANDLER_CALLBACKS_NAME[] = "RegisterEventHandlerCallbacks";
40 
41 using InitCJRuntimeType = int(*)(const struct RuntimeParam*);;
42 using InitUISchedulerType = void*(*)();
43 using RunUISchedulerType = int(*)(unsigned long long);
44 using FiniCJRuntimeType = int(*)();
45 using InitCJLibraryType = int(*)(const char*);
46 using RegisterEventHandlerType = void(*)(PostTaskType, HasHigherPriorityType);
47 
48 #ifdef __OHOS__
49 const char REGISTER_UNCAUGHT_EXCEPTION_NAME[] = "RegisterUncaughtExceptionHandler";
50 using RegisterUncaughtExceptionType = void (*)(const CJUncaughtExceptionInfo& handle);
51 #endif
52 
53 #ifdef WITH_EVENT_HANDLER
54 static std::shared_ptr<AppExecFwk::EventHandler> g_handler = nullptr;
55 #endif
56 
LoadSymbolInitCJRuntime(void * handle,CJRuntimeAPI & apis)57 bool LoadSymbolInitCJRuntime(void* handle, CJRuntimeAPI& apis)
58 {
59     auto symbol = DynamicFindSymbol(handle, INIT_CJRUNTIME_SYMBOL_NAME);
60     if (symbol == nullptr) {
61         LOGE("runtime api not found: %{public}s", INIT_CJRUNTIME_SYMBOL_NAME);
62         return false;
63     }
64     apis.InitCJRuntime = reinterpret_cast<InitCJRuntimeType>(symbol);
65     return true;
66 }
67 
LoadSymbolInitUIScheduler(void * handle,CJRuntimeAPI & apis)68 bool LoadSymbolInitUIScheduler(void* handle, CJRuntimeAPI& apis)
69 {
70     auto symbol = DynamicFindSymbol(handle, INIT_UISCHEDULER_SYMBOL_NAME);
71     if (symbol == nullptr) {
72         LOGE("runtime api not found: %{public}s", INIT_UISCHEDULER_SYMBOL_NAME);
73         return false;
74     }
75     apis.InitUIScheduler = reinterpret_cast<InitUISchedulerType>(symbol);
76     return true;
77 }
78 
LoadSymbolRunUIScheduler(void * handle,CJRuntimeAPI & apis)79 bool LoadSymbolRunUIScheduler(void* handle, CJRuntimeAPI& apis)
80 {
81     auto symbol = DynamicFindSymbol(handle, RUN_UISCHEDULER_SYMBOL_NAME);
82     if (symbol == nullptr) {
83         LOGE("runtime api not found: %{public}s", RUN_UISCHEDULER_SYMBOL_NAME);
84         return false;
85     }
86     apis.RunUIScheduler = reinterpret_cast<RunUISchedulerType>(symbol);
87     return true;
88 }
89 
LoadSymbolFiniCJRuntime(void * handle,CJRuntimeAPI & apis)90 bool LoadSymbolFiniCJRuntime(void* handle, CJRuntimeAPI& apis)
91 {
92     auto symbol = DynamicFindSymbol(handle, FINI_CJRUNTIME_SYMBOL_NAME);
93     if (symbol == nullptr) {
94         LOGE("runtime api not found: %{public}s", FINI_CJRUNTIME_SYMBOL_NAME);
95         return false;
96     }
97     apis.FiniCJRuntime = reinterpret_cast<FiniCJRuntimeType>(symbol);
98     return true;
99 }
100 
LoadSymbolInitCJLibrary(void * handle,CJRuntimeAPI & apis)101 bool LoadSymbolInitCJLibrary(void* handle, CJRuntimeAPI& apis)
102 {
103     auto symbol = DynamicFindSymbol(handle, INIT_CJLIBRARY_SYMBOL_NAME);
104     if (symbol == nullptr) {
105         LOGE("runtime api not found: %{public}s", INIT_CJLIBRARY_SYMBOL_NAME);
106         return false;
107     }
108     apis.InitCJLibrary = reinterpret_cast<InitCJLibraryType>(symbol);
109     return true;
110 }
111 
LoadSymbolRegisterEventHandlerCallbacks(void * handle,CJRuntimeAPI & apis)112 bool LoadSymbolRegisterEventHandlerCallbacks(void* handle, CJRuntimeAPI& apis)
113 {
114     auto symbol = DynamicFindSymbol(handle, REGISTER_EVENTHANDLER_CALLBACKS_NAME);
115     if (symbol == nullptr) {
116         LOGE("runtime api not found: %{public}s", REGISTER_EVENTHANDLER_CALLBACKS_NAME);
117         return false;
118     }
119     apis.RegisterEventHandlerCallbacks = reinterpret_cast<RegisterEventHandlerType>(symbol);
120     return true;
121 }
122 
123 #ifdef __OHOS__
LoadSymbolRegisterCJUncaughtExceptionHandler(void * handle,CJRuntimeAPI & apis)124 bool LoadSymbolRegisterCJUncaughtExceptionHandler(void* handle, CJRuntimeAPI& apis)
125 {
126     auto symbol = DynamicFindSymbol(handle, REGISTER_UNCAUGHT_EXCEPTION_NAME);
127     if (symbol == nullptr) {
128         LOGE("runtime api not found: %{public}s", REGISTER_UNCAUGHT_EXCEPTION_NAME);
129         return false;
130     }
131     apis.RegisterCJUncaughtExceptionHandler = reinterpret_cast<RegisterUncaughtExceptionType>(symbol);
132     return true;
133 }
134 #endif
135 
PostTaskWrapper(void * func)136 bool PostTaskWrapper(void* func)
137 {
138     return CJEnvironment::GetInstance()->PostTask(reinterpret_cast<TaskFuncType>(func));
139 }
140 
HasHigherPriorityTaskWrapper()141 bool HasHigherPriorityTaskWrapper()
142 {
143     return CJEnvironment::GetInstance()->HasHigherPriorityTask();
144 }
145 } // namespace
146 
147 const char *CJEnvironment::cjAppNSName = "cj_app";
148 const char *CJEnvironment::cjSDKNSName = "cj_sdk";
149 const char *CJEnvironment::cjSysNSName = "cj_system";
150 const char *CJEnvironment::cjChipSDKNSName = "cj_chipsdk";
151 
152 CJRuntimeAPI CJEnvironment::lazyApis_ {};
153 
GetInstance()154 CJEnvironment* CJEnvironment::GetInstance()
155 {
156     static CJEnvironment cjEnv;
157 #ifdef WITH_EVENT_HANDLER
158     if (g_handler == nullptr) {
159         g_handler = std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner());
160     }
161 #endif
162     return &cjEnv;
163 }
164 
LoadRuntimeApis()165 bool CJEnvironment::LoadRuntimeApis()
166 {
167     static bool isRuntimeApiLoaded {false};
168     if (isRuntimeApiLoaded) {
169         return true;
170     }
171 #ifdef __WINDOWS__
172 #define RTLIB_NAME "libcangjie-runtime.dll"
173 #else
174 #define RTLIB_NAME "libcangjie-runtime.so"
175 #endif
176 #ifdef __OHOS__
177     Dl_namespace ns;
178     dlns_get(CJEnvironment::cjSDKNSName, &ns);
179     std::string runtimeLibName = "libcangjie-runtime";
180     if (sanitizerKind_ == SanitizerKind::ASAN) {
181         runtimeLibName += "_asan";
182     } else if (sanitizerKind_ == SanitizerKind::TSAN) {
183         runtimeLibName += "_tsan";
184     } else if (sanitizerKind_ == SanitizerKind::HWASAN) {
185         runtimeLibName += "_hwasan";
186     }
187     runtimeLibName += ".so";
188     auto dso = DynamicLoadLibrary(&ns, runtimeLibName.c_str(), 1);
189 #else
190     auto dso = DynamicLoadLibrary(RTLIB_NAME, 1);
191 #endif
192     if (!dso) {
193         LOGE("load library failed: %{public}s", RTLIB_NAME);
194         return false;
195     }
196 #undef RTLIB_NAME
197     if (!LoadSymbolInitCJRuntime(dso, lazyApis_) ||
198         !LoadSymbolInitUIScheduler(dso, lazyApis_) ||
199         !LoadSymbolRunUIScheduler(dso, lazyApis_) ||
200         !LoadSymbolFiniCJRuntime(dso, lazyApis_) ||
201         !LoadSymbolInitCJLibrary(dso, lazyApis_) ||
202         !LoadSymbolRegisterEventHandlerCallbacks(dso, lazyApis_)) {
203         LOGE("load symbol failed");
204         return false;
205     }
206 #ifdef __OHOS__
207     if (!LoadSymbolRegisterCJUncaughtExceptionHandler(dso, lazyApis_)) {
208         LOGE("load symbol RegisterCJUncaughtExceptionHandler failed");
209         return false;
210     }
211 #endif
212     isRuntimeApiLoaded = true;
213     return true;
214 }
215 
RegisterCJUncaughtExceptionHandler(const CJUncaughtExceptionInfo & handle)216 void CJEnvironment::RegisterCJUncaughtExceptionHandler(const CJUncaughtExceptionInfo& handle)
217 {
218     lazyApis_.RegisterCJUncaughtExceptionHandler(handle);
219 }
220 
PostTask(TaskFuncType task)221 bool CJEnvironment::PostTask(TaskFuncType task)
222 {
223 #ifdef WITH_EVENT_HANDLER
224     if (task == nullptr) {
225         LOGE("null task could not be posted");
226         return false;
227     }
228 
229     bool postDone = g_handler->PostTask(task, "spawn-main-task-from-cj", 0, AppExecFwk::EventQueue::Priority::HIGH);
230     if (!postDone) {
231         LOGE("event handler support cj ui scheduler");
232         return false;
233     }
234     return true;
235 #endif
236     return true;
237 }
238 
HasHigherPriorityTask()239 bool CJEnvironment::HasHigherPriorityTask()
240 {
241 #ifdef WITH_EVENT_HANDLER
242     return g_handler->HasPreferEvent(static_cast<int>(AppExecFwk::EventQueue::Priority::HIGH));
243 #endif
244     return false;
245 }
246 
InitCJChipSDKNS(const std::string & path)247 void CJEnvironment::InitCJChipSDKNS(const std::string& path)
248 {
249 #ifdef __OHOS__
250     LOGI("InitCJChipSDKNS: %{public}s", path.c_str());
251     Dl_namespace chip_ndk;
252     DynamicInitNamespace(&chip_ndk, nullptr, path.c_str(), CJEnvironment::cjChipSDKNSName);
253 
254     Dl_namespace ndk;
255     Dl_namespace current;
256     dlns_get(nullptr, &current);
257     dlns_get("ndk", &ndk);
258     dlns_inherit(&chip_ndk, &ndk, "allow_all_shared_libs");
259     dlns_inherit(&chip_ndk, &current, "allow_all_shared_libs");
260 #endif
261 }
262 
263 // Init app namespace
InitCJAppNS(const std::string & path)264 void CJEnvironment::InitCJAppNS(const std::string& path)
265 {
266 #ifdef __OHOS__
267     LOGI("InitCJAppNS: %{public}s", path.c_str());
268     Dl_namespace ndk;
269     Dl_namespace ns;
270     DynamicInitNamespace(&ns, nullptr, path.c_str(), CJEnvironment::cjAppNSName);
271     dlns_get("ndk", &ndk);
272     dlns_inherit(&ns, &ndk, "allow_all_shared_libs");
273     Dl_namespace current;
274     dlns_get(nullptr, &current);
275     dlns_inherit(&ndk, &current, "allow_all_shared_libs");
276     dlns_inherit(&current, &ndk, "allow_all_shared_libs");
277 #endif
278 }
279 
280 // Init cj sdk namespace
InitCJSDKNS(const std::string & path)281 void CJEnvironment::InitCJSDKNS(const std::string& path)
282 {
283 #ifdef __OHOS__
284     LOGI("InitCJSDKNS: %{public}s", path.c_str());
285     Dl_namespace cj_app;
286     Dl_namespace ns;
287     dlns_get(CJEnvironment::cjAppNSName, &cj_app);
288     DynamicInitNamespace(&ns, &cj_app, path.c_str(), CJEnvironment::cjSDKNSName);
289 #endif
290 }
291 
292 // Init cj system namespace
InitCJSysNS(const std::string & path)293 void CJEnvironment::InitCJSysNS(const std::string& path)
294 {
295 #ifdef __OHOS__
296     LOGI("InitCJSysNS: %{public}s", path.c_str());
297     Dl_namespace cj_sdk;
298     Dl_namespace ndk;
299     Dl_namespace ns;
300     dlns_get(CJEnvironment::cjSDKNSName, &cj_sdk);
301     DynamicInitNamespace(&ns, &cj_sdk, path.c_str(), CJEnvironment::cjSysNSName);
302     dlns_get("ndk", &ndk);
303     dlns_inherit(&ns, &ndk, "allow_all_shared_libs");
304 #endif
305 }
306 
StartRuntime()307 bool CJEnvironment::StartRuntime()
308 {
309     if (isRuntimeStarted_) {
310         return true;
311     }
312 
313     if (!LoadRuntimeApis()) {
314         LOGE("LoadRuntimeApis failed");
315         return false;
316     }
317 
318     RuntimeParam rtParams {
319         .heapParam = {
320             .regionSize = 64,
321             .heapSize = 256 * 1024,
322             .exemptionThreshold= 0.8,
323             .heapUtilization = 0.8,
324             .heapGrowth = 0.15,
325             .allocationRate = 0,
326             .allocationWaitTime = 0,
327         },
328         .gcParam = {
329             .gcThreshold = 0,
330             .garbageThreshold = 0,
331             .gcInterval = 0,
332             .backupGCInterval = 0,
333             .gcThreads = 0,
334         },
335         .logParam = {
336             .logLevel = RTLOG_ERROR,
337         },
338         .coParam = {
339             .thStackSize = 2 * 1024,
340             .coStackSize = 2 * 1024,
341             .processorNum = 8,
342         }
343     };
344 
345     auto status = lazyApis_.InitCJRuntime(&rtParams);
346     if (status != E_OK) {
347         LOGE("init cj runtime failed: %{public}d", status);
348         return false;
349     }
350 
351     lazyApis_.RegisterEventHandlerCallbacks(PostTaskWrapper, HasHigherPriorityTaskWrapper);
352 
353     isRuntimeStarted_ = true;
354     return true;
355 }
356 
StopRuntime()357 void CJEnvironment::StopRuntime()
358 {
359     if (!isRuntimeStarted_) {
360         return;
361     }
362 
363     if (isUISchedulerStarted_) {
364         StopUIScheduler();
365     }
366 
367     auto code = lazyApis_.FiniCJRuntime();
368     if (code == E_OK) {
369         isRuntimeStarted_ = false;
370     }
371 }
372 
StartUIScheduler()373 bool CJEnvironment::StartUIScheduler()
374 {
375     if (isUISchedulerStarted_) {
376         return true;
377     }
378 
379     uiScheduler_ = lazyApis_.InitUIScheduler();
380     if (!uiScheduler_) {
381         LOGE("init cj ui scheduler failed");
382         return false;
383     }
384 
385     isUISchedulerStarted_ = true;
386     return true;
387 }
388 
StopUIScheduler()389 void CJEnvironment::StopUIScheduler()
390 {
391     isUISchedulerStarted_ = false;
392 }
393 
LoadCJLibrary(const char * dlName)394 void* CJEnvironment::LoadCJLibrary(const char* dlName)
395 {
396     if (!StartRuntime()) {
397         LOGE("StartRuntime failed");
398         return nullptr;
399     }
400     auto handle = LoadCJLibrary(APP, dlName);
401     if (!handle) {
402         LOGE("load cj library failed: %{public}s", DynamicGetError());
403         return nullptr;
404     }
405 
406     LOGI("LoadCJLibrary InitCJLibrary: %{public}s", dlName);
407     auto status = lazyApis_.InitCJLibrary(dlName);
408     if (status != E_OK) {
409         LOGE("InitCJLibrary failed: %{public}s", dlName);
410         UnLoadCJLibrary(handle);
411         return nullptr;
412     }
413 
414     return handle;
415 }
416 
LoadCJLibrary(OHOS::CJEnvironment::LibraryKind kind,const char * dlName)417 void* CJEnvironment::LoadCJLibrary(OHOS::CJEnvironment::LibraryKind kind, const char* dlName)
418 {
419 #ifdef __OHOS__
420     Dl_namespace ns;
421     switch (kind) {
422         case APP:
423             dlns_get(CJEnvironment::cjAppNSName, &ns);
424             break;
425         case SYSTEM:
426             dlns_get(CJEnvironment::cjSysNSName, &ns);
427             break;
428         case SDK:
429             dlns_get(CJEnvironment::cjSDKNSName, &ns);
430             break;
431     }
432     auto handle = DynamicLoadLibrary(&ns, dlName, 1);
433 #else
434     auto handle = DynamicLoadLibrary(dlName, 1);
435 #endif
436     if (!handle) {
437         LOGE("load cj library failed: %{public}s", DynamicGetError());
438         return nullptr;
439     }
440     return handle;
441 }
442 
UnLoadCJLibrary(void * handle)443 void CJEnvironment::UnLoadCJLibrary(void* handle)
444 {
445     DynamicFreeLibrary(handle);
446 }
447 
GetSymbol(void * dso,const char * symbol)448 void* CJEnvironment::GetSymbol(void* dso, const char* symbol)
449 {
450     return DynamicFindSymbol(dso, symbol);
451 }
452 
StartDebugger()453 bool CJEnvironment::StartDebugger()
454 {
455 #ifdef __OHOS__
456     Dl_namespace ns;
457     dlns_get(CJEnvironment::cjSysNSName, &ns);
458     auto handle = DynamicLoadLibrary(&ns, DEBUGGER_LIBNAME, 0);
459 #else
460     auto handle = DynamicLoadLibrary(DEBUGGER_LIBNAME, 0);
461 #endif
462     if (!handle) {
463         LOGE("failed to load library: %{public}s", DEBUGGER_LIBNAME);
464         return false;
465     }
466     auto symbol = DynamicFindSymbol(handle, DEBUGGER_SYMBOL_NAME);
467     if (!symbol) {
468         LOGE("failed to find symbol: %{public}s", DEBUGGER_SYMBOL_NAME);
469         DynamicFreeLibrary(handle);
470         return false;
471     }
472     auto func = reinterpret_cast<bool (*)(int, const std::string&)>(symbol);
473     std::string name = "PandaDebugger";
474     func(0, name);
475     return true;
476 }
477 
OHOS_GetCJEnvInstance()478 CJ_EXPORT extern "C" CJEnvMethods* OHOS_GetCJEnvInstance()
479 {
480     static CJEnvMethods gCJEnvMethods {
481         .initCJAppNS = [](const std::string& path) {
482             CJEnvironment::GetInstance()->InitCJAppNS(path);
483         },
484         .initCJSDKNS = [](const std::string& path) {
485             CJEnvironment::GetInstance()->InitCJSDKNS(path);
486         },
487         .initCJSysNS = [](const std::string& path) {
488             CJEnvironment::GetInstance()->InitCJSysNS(path);
489         },
490         .initCJChipSDKNS = [](const std::string& path) {
491             CJEnvironment::GetInstance()->InitCJChipSDKNS(path);
492         },
493         .startRuntime = [] {
494             return CJEnvironment::GetInstance()->StartRuntime();
495         },
496         .startUIScheduler = [] {
497             return CJEnvironment::GetInstance()->StartUIScheduler();
498         },
499         .loadCJModule = [](const char* dllName) {
500             return CJEnvironment::GetInstance()->LoadCJLibrary(dllName);
501         },
502         .loadLibrary = [](uint32_t kind, const char* dllName) {
503             return CJEnvironment::GetInstance()->LoadCJLibrary(static_cast<CJEnvironment::LibraryKind>(kind), dllName);
504         },
505         .getSymbol = [](void* handle, const char* dllName) {
506             return CJEnvironment::GetInstance()->GetSymbol(handle, dllName);
507         },
508         .loadCJLibrary = [](const char* dllName) {
509             return CJEnvironment::GetInstance()->LoadCJLibrary(dllName);
510         },
511         .startDebugger = []() {
512             return CJEnvironment::GetInstance()->StartDebugger();
513         },
514         .registerCJUncaughtExceptionHandler = [](const CJUncaughtExceptionInfo& handle) {
515             return CJEnvironment::GetInstance()->RegisterCJUncaughtExceptionHandler(handle);
516         },
517         .setSanitizerKindRuntimeVersion = [](SanitizerKind kind) {
518             return CJEnvironment::GetInstance()->SetSanitizerKindRuntimeVersion(kind);
519         }
520     };
521     return &gCJEnvMethods;
522 }
523 }
524