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, ¤t);
257 dlns_get("ndk", &ndk);
258 dlns_inherit(&chip_ndk, &ndk, "allow_all_shared_libs");
259 dlns_inherit(&chip_ndk, ¤t, "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, ¤t);
275 dlns_inherit(&ndk, ¤t, "allow_all_shared_libs");
276 dlns_inherit(¤t, &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