1 /*
2 * Copyright (c) 2021 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 "native_module_manager.h"
17
18 #include "native_engine/native_engine.h"
19
20 #include <dirent.h>
21
22 #include "securec.h"
23 #include "utils/log.h"
24
25 namespace {
26 constexpr static int32_t NATIVE_PATH_NUMBER = 2;
27 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(__BIONIC__)
28 constexpr static char DL_NAMESPACE[] = "ace";
29 #endif
30 } // namespace
31
32 NativeModuleManager NativeModuleManager::instance_;
33
NativeModuleManager()34 NativeModuleManager::NativeModuleManager()
35 {
36 firstNativeModule_ = nullptr;
37 lastNativeModule_ = nullptr;
38 appLibPath_ = nullptr;
39
40 pthread_mutex_init(&mutex_, nullptr);
41 }
42
~NativeModuleManager()43 NativeModuleManager::~NativeModuleManager()
44 {
45 NativeModule* nativeModule = firstNativeModule_;
46 while (nativeModule != nullptr) {
47 nativeModule = nativeModule->next;
48 delete firstNativeModule_;
49 firstNativeModule_ = nativeModule;
50 }
51 firstNativeModule_ = lastNativeModule_ = nullptr;
52 if (appLibPath_) {
53 delete[] appLibPath_;
54 }
55
56 pthread_mutex_destroy(&mutex_);
57 }
58
GetInstance()59 NativeModuleManager* NativeModuleManager::GetInstance()
60 {
61 return &instance_;
62 }
63
Register(NativeModule * nativeModule)64 void NativeModuleManager::Register(NativeModule* nativeModule)
65 {
66 if (nativeModule == nullptr) {
67 HILOG_ERROR("nativeModule value is null");
68 return;
69 }
70
71 if (firstNativeModule_ == lastNativeModule_ && lastNativeModule_ == nullptr) {
72 firstNativeModule_ = new NativeModule();
73 if (firstNativeModule_ == nullptr) {
74 HILOG_ERROR("first NativeModule create failed");
75 return;
76 }
77 lastNativeModule_ = firstNativeModule_;
78 } else {
79 auto next = new NativeModule();
80 if (next == nullptr) {
81 HILOG_ERROR("next NativeModule create failed");
82 return;
83 }
84 lastNativeModule_->next = next;
85 lastNativeModule_ = lastNativeModule_->next;
86 }
87
88 lastNativeModule_->version = nativeModule->version;
89 lastNativeModule_->fileName = nativeModule->fileName;
90 lastNativeModule_->name = nativeModule->name;
91 lastNativeModule_->refCount = nativeModule->refCount;
92 lastNativeModule_->registerCallback = nativeModule->registerCallback;
93 lastNativeModule_->next = nullptr;
94 }
95
CreateLdNamespace(const char * lib_ld_path)96 void NativeModuleManager::CreateLdNamespace(const char* lib_ld_path)
97 {
98 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(__BIONIC__)
99 dlns_init(&ns_, DL_NAMESPACE);
100 dlns_create(&ns_, lib_ld_path);
101 #endif
102 }
103
SetAppLibPath(const char * appLibPath)104 void NativeModuleManager::SetAppLibPath(const char* appLibPath)
105 {
106 HILOG_INFO("create ld namespace, path: %{private}s", appLibPath);
107 char* tmp = new char[NAPI_PATH_MAX];
108 errno_t err = EOK;
109 err = memset_s(tmp, NAPI_PATH_MAX, 0, NAPI_PATH_MAX);
110 if (err != EOK) {
111 delete[] tmp;
112 return;
113 }
114 err = strcpy_s(tmp, NAPI_PATH_MAX, appLibPath);
115 if (err != EOK) {
116 delete[] tmp;
117 return;
118 }
119 if (appLibPath_ != nullptr) {
120 delete[] appLibPath_;
121 }
122 appLibPath_ = tmp;
123 CreateLdNamespace(appLibPath_);
124 }
125
LoadNativeModule(const char * moduleName,const char * path,bool isAppModule,bool internal,bool isArk)126 NativeModule* NativeModuleManager::LoadNativeModule(const char* moduleName,
127 const char* path, bool isAppModule, bool internal, bool isArk)
128 {
129 if (moduleName == nullptr) {
130 HILOG_ERROR("moduleName value is null");
131 return nullptr;
132 }
133
134 if (pthread_mutex_lock(&mutex_) != 0) {
135 HILOG_ERROR("pthread_mutex_lock is failed");
136 return nullptr;
137 }
138
139 NativeModule* nativeModule = FindNativeModuleByCache(moduleName);
140 if (nativeModule == nullptr) {
141 HILOG_INFO("not in cache: moduleName: %{public}s", moduleName);
142 nativeModule = FindNativeModuleByDisk(moduleName, internal, isAppModule, isArk);
143 }
144
145 if (pthread_mutex_unlock(&mutex_) != 0) {
146 HILOG_ERROR("pthread_mutex_unlock is failed");
147 return nullptr;
148 }
149
150 return nativeModule;
151 }
152
GetNativeModulePath(const char * moduleName,const bool isAppModule,char nativeModulePath[][NAPI_PATH_MAX],int32_t pathLength) const153 bool NativeModuleManager::GetNativeModulePath(
154 const char* moduleName, const bool isAppModule, char nativeModulePath[][NAPI_PATH_MAX], int32_t pathLength) const
155 {
156 #ifdef WINDOWS_PLATFORM
157 const char* soPostfix = ".dll";
158 const char* sysPrefix = "./module";
159 const char* zfix = "";
160 #elif defined(MAC_PLATFORM)
161 const char* soPostfix = ".dylib";
162 const char* sysPrefix = "./module";
163 const char* zfix = "";
164 #elif defined(_ARM64_)
165 const char* soPostfix = ".so";
166 const char* sysPrefix = "/system/lib64/module";
167 const char* zfix = ".z";
168 #else
169 const char* soPostfix = ".so";
170 const char* sysPrefix = "/system/lib/module";
171 const char* zfix = ".z";
172 #endif
173 int32_t lengthOfModuleName = strlen(moduleName);
174 char dupModuleName[NAPI_PATH_MAX] = { 0 };
175 if (strcpy_s(dupModuleName, NAPI_PATH_MAX, moduleName) != 0) {
176 HILOG_ERROR("strcpy moduleName failed");
177 return false;
178 }
179
180 const char* prefix = nullptr;
181 if (isAppModule && appLibPath_) {
182 prefix = appLibPath_;
183 } else {
184 prefix = sysPrefix;
185 for (int32_t i = 0; i < lengthOfModuleName; i++) {
186 dupModuleName[i] = tolower(dupModuleName[i]);
187 }
188 }
189
190 int32_t lengthOfPostfix = strlen(soPostfix);
191 if ((lengthOfModuleName > lengthOfPostfix) &&
192 (strcmp(dupModuleName + lengthOfModuleName - lengthOfPostfix, soPostfix) == 0)) {
193 if (sprintf_s(nativeModulePath[0], pathLength, "%s/%s", prefix, dupModuleName) == -1) {
194 return false;
195 }
196 return true;
197 }
198
199 char* lastDot = strrchr(dupModuleName, '.');
200 if (lastDot == nullptr) {
201 if (!isAppModule || !appLibPath_) {
202 if (sprintf_s(nativeModulePath[0], pathLength, "%s/lib%s%s%s",
203 prefix, dupModuleName, zfix, soPostfix) == -1) {
204 return false;
205 }
206 if (sprintf_s(nativeModulePath[1], pathLength, "%s/lib%s_napi%s%s",
207 prefix, dupModuleName, zfix, soPostfix) == -1) {
208 return false;
209 }
210 } else {
211 if (sprintf_s(nativeModulePath[0], pathLength, "%s/lib%s%s",
212 prefix, dupModuleName, soPostfix) == -1) {
213 return false;
214 }
215 }
216 } else {
217 char* afterDot = lastDot + 1;
218 if (*afterDot == '\0') {
219 return false;
220 }
221 *lastDot = '\0';
222 lengthOfModuleName = strlen(dupModuleName);
223 for (int32_t i = 0; i < lengthOfModuleName; i++) {
224 if (*(dupModuleName + i) == '.') {
225 *(dupModuleName + i) = '/';
226 }
227 }
228 if (!isAppModule || !appLibPath_) {
229 if (sprintf_s(nativeModulePath[0], pathLength, "%s/%s/lib%s%s%s",
230 prefix, dupModuleName, afterDot, zfix, soPostfix) == -1) {
231 return false;
232 }
233 if (sprintf_s(nativeModulePath[1], pathLength, "%s/%s/lib%s_napi%s%s",
234 prefix, dupModuleName, afterDot, zfix, soPostfix) == -1) {
235 return false;
236 }
237 } else {
238 if (sprintf_s(nativeModulePath[0], pathLength, "%s/%s/lib%s%s",
239 prefix, dupModuleName, afterDot, soPostfix) == -1) {
240 return false;
241 }
242 }
243 }
244 return true;
245 }
246
LoadModuleLibrary(const char * path,const bool isAppModule)247 LIBHANDLE NativeModuleManager::LoadModuleLibrary(const char* path, const bool isAppModule)
248 {
249 if (strlen(path) == 0) {
250 HILOG_ERROR("primary module path is empty");
251 return nullptr;
252 }
253 LIBHANDLE lib = nullptr;
254 #if defined(WINDOWS_PLATFORM)
255 lib = LoadLibrary(path);
256 if (lib == nullptr) {
257 HILOG_ERROR("LoadLibrary failed, error: %{public}d", GetLastError());
258 }
259 #elif defined(MAC_PLATFORM) || defined(__BIONIC__)
260 lib = dlopen(path, RTLD_LAZY);
261 if (lib == nullptr) {
262 HILOG_ERROR("dlopen failed: %{public}s", dlerror());
263 }
264 #else
265 if (isAppModule) {
266 lib = dlopen_ns(&ns_, path, RTLD_LAZY);
267 } else {
268 lib = dlopen(path, RTLD_LAZY);
269 }
270 if (lib == nullptr) {
271 HILOG_ERROR("dlopen failed: %{public}s", dlerror());
272 }
273 #endif
274 return lib;
275 }
276
277 using NAPIGetJSCode = void (*)(const char** buf, int* bufLen);
FindNativeModuleByDisk(const char * moduleName,bool internal,const bool isAppModule,bool isArk)278 NativeModule* NativeModuleManager::FindNativeModuleByDisk(
279 const char* moduleName, bool internal, const bool isAppModule, bool isArk)
280 {
281 char nativeModulePath[NATIVE_PATH_NUMBER][NAPI_PATH_MAX];
282 nativeModulePath[0][0] = 0;
283 nativeModulePath[1][0] = 0;
284 if (!GetNativeModulePath(moduleName, isAppModule, nativeModulePath, NAPI_PATH_MAX)) {
285 HILOG_ERROR("get module filed");
286 return nullptr;
287 }
288
289 // load primary module path first
290 char* loadPath = nativeModulePath[0];
291 HILOG_INFO("get primary module path: %{public}s", loadPath);
292 LIBHANDLE lib = LoadModuleLibrary(loadPath, isAppModule);
293 if (lib == nullptr) {
294 loadPath = nativeModulePath[1];
295 HILOG_WARN("primary module path load failed, try to load secondary module path: %{public}s", loadPath);
296 lib = LoadModuleLibrary(loadPath, isAppModule);
297 if (lib == nullptr) {
298 HILOG_ERROR("secondary module path load failed, load native module failed");
299 return nullptr;
300 }
301 }
302
303 if (lastNativeModule_ && strcmp(lastNativeModule_->name, moduleName)) {
304 HILOG_WARN("moduleName '%{public}s' does not match plugin's name '%{public}s'",
305 moduleName, lastNativeModule_->name);
306 }
307
308 if (!internal) {
309 char symbol[NAPI_PATH_MAX] = { 0 };
310 if (!isArk) {
311 if (sprintf_s(symbol, sizeof(symbol), "NAPI_%s_GetJSCode", moduleName) == -1) {
312 LIBFREE(lib);
313 return nullptr;
314 }
315 } else {
316 if (sprintf_s(symbol, sizeof(symbol), "NAPI_%s_GetABCCode", moduleName) == -1) {
317 LIBFREE(lib);
318 return nullptr;
319 }
320 }
321
322 // replace '.' with '_'
323 for (char* p = strchr(symbol, '.'); p != nullptr; p = strchr(p + 1, '.')) {
324 *p = '_';
325 }
326
327
328 auto getJSCode = reinterpret_cast<NAPIGetJSCode>(LIBSYM(lib, symbol));
329 if (getJSCode != nullptr) {
330 const char* buf = nullptr;
331 int bufLen = 0;
332 getJSCode(&buf, &bufLen);
333 if (lastNativeModule_ != nullptr) {
334 HILOG_INFO("get js code from module: bufLen: %{public}d", bufLen);
335 lastNativeModule_->jsCode = buf;
336 lastNativeModule_->jsCodeLen = bufLen;
337 }
338 } else {
339 HILOG_INFO("ignore: no %{public}s in %{public}s", symbol, loadPath);
340 }
341 }
342 if (lastNativeModule_) {
343 lastNativeModule_->moduleLoaded = true;
344 }
345 return lastNativeModule_;
346 }
347
FindNativeModuleByCache(const char * moduleName)348 NativeModule* NativeModuleManager::FindNativeModuleByCache(const char* moduleName)
349 {
350 NativeModule* result = nullptr;
351 NativeModule* preNativeModule = nullptr;
352 for (NativeModule* temp = firstNativeModule_; temp != nullptr; temp = temp->next) {
353 if (!strcasecmp(temp->name, moduleName)) {
354 if (strcmp(temp->name, moduleName)) {
355 HILOG_WARN("moduleName '%{public}s' does not match plugin's name '%{public}s'",
356 moduleName, temp->name);
357 }
358 result = temp;
359 break;
360 }
361 preNativeModule = temp;
362 }
363
364 if (result && !result->moduleLoaded) {
365 if (preNativeModule) {
366 preNativeModule->next = result->next;
367 } else {
368 firstNativeModule_ = firstNativeModule_->next;
369 }
370 result->next = nullptr;
371 lastNativeModule_->next = result;
372 lastNativeModule_ = result;
373 return nullptr;
374 }
375 return result;
376 }
377