1 /**
2 * Copyright (c) 2021-2025 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 "plugins/ets/runtime/ets_native_library_provider.h"
17
18 #include "ets_native_library.h"
19 #include "include/mem/panda_containers.h"
20 #include "include/mem/panda_string.h"
21 #include "plugins/ets/runtime/ani/ani.h"
22 #include "plugins/ets/runtime/ani/ani_interaction_api.h"
23 #include "plugins/ets/runtime/ets_vm.h"
24 #include "plugins/ets/runtime/napi/ets_napi_invoke_interface.h"
25 #include "plugins/ets/runtime/types/ets_method.h"
26 #include "plugins/ets/runtime/ets_namespace_manager_impl.h"
27
28 namespace ark::ets {
29
30 namespace {
31
LoadFromPath(const PandaVector<PandaString> & pathes,const PandaString & name)32 Expected<EtsNativeLibrary, os::Error> LoadFromPath(const PandaVector<PandaString> &pathes, const PandaString &name)
33 {
34 if (name.find('/') == std::string::npos) {
35 for (const auto &path : pathes) {
36 if (path.empty()) {
37 continue;
38 }
39 PandaStringStream s;
40 s << path << '/' << name;
41 if (auto loadRes = EtsNativeLibrary::Load(s.str())) {
42 return loadRes;
43 }
44 }
45 }
46 return EtsNativeLibrary::Load(name);
47 }
48
LoadNativeLibraryFromNamespace(const char * name)49 Expected<EtsNativeLibrary, os::Error> LoadNativeLibraryFromNamespace(const char *name)
50 {
51 auto coroutine = EtsCoroutine::GetCurrent();
52 std::string abcPath;
53 for (auto stack = StackWalker::Create(coroutine); stack.HasFrame(); stack.NextFrame()) {
54 auto *method = stack.GetMethod();
55 if (LIKELY(method != nullptr)) {
56 if (method->GetPandaFile() == nullptr) {
57 continue;
58 }
59 auto *ctx = method->GetClass()->GetLoadContext();
60 ASSERT(ctx != nullptr);
61 LOG(INFO, RUNTIME) << "NativeLibraryProvider::LoadNativeLibraryFromNamespace: ctx:"
62 << reinterpret_cast<uint64_t>(ctx)
63 << ", isBootContext:" << ((ctx->IsBootContext()) ? "true" : "false");
64 if (!ctx->IsBootContext()) {
65 abcPath = method->GetPandaFile()->GetFullFileName();
66 break;
67 }
68 }
69 }
70 EtsNamespaceManagerImpl &instance = EtsNamespaceManagerImpl::GetInstance();
71 return instance.LoadNativeLibraryFromNs(abcPath, name);
72 }
73 } // namespace
74
LoadLibrary(EtsEnv * env,const PandaString & name,bool shouldVerifyPermission,const PandaString & fileName)75 std::optional<std::string> NativeLibraryProvider::LoadLibrary(EtsEnv *env, const PandaString &name,
76 bool shouldVerifyPermission, const PandaString &fileName)
77 {
78 if (shouldVerifyPermission && !CheckLibraryPermission(env, fileName)) {
79 return "NativeLibraryProvider::CheckLibraryPermission failed";
80 }
81 {
82 os::memory::ReadLockHolder lock(lock_);
83 auto it =
84 std::find_if(libraries_.begin(), libraries_.end(), [&name](auto &lib) { return lib.GetName() == name; });
85 if (it != libraries_.end()) {
86 return {};
87 }
88 }
89 auto loadRes = LoadFromPath(GetLibraryPath(), name);
90 if (!shouldVerifyPermission && !loadRes) {
91 LOG(WARNING, RUNTIME) << "Failed to load System library: " << loadRes.Error().ToString()
92 << "; Attempting to load application library instead.";
93 loadRes = LoadNativeLibraryFromNamespace(name.c_str());
94 }
95 if (!loadRes) {
96 return loadRes.Error().ToString();
97 }
98 const EtsNativeLibrary *lib = nullptr;
99 {
100 os::memory::WriteLockHolder lock(lock_);
101 auto [it, inserted] = libraries_.emplace(std::move(loadRes.Value()));
102 if (!inserted) {
103 return {};
104 }
105 lib = &*it;
106 }
107 ASSERT(lib != nullptr);
108 return CallAniCtor(env, lib);
109 }
110
CallAniCtor(EtsEnv * env,const EtsNativeLibrary * lib)111 std::optional<std::string> NativeLibraryProvider::CallAniCtor(EtsEnv *env, const EtsNativeLibrary *lib)
112 {
113 if (auto onLoadSymbol = lib->FindSymbol("EtsNapiOnLoad")) {
114 using EtsNapiOnLoadHandle = ets_int (*)(EtsEnv *);
115 auto onLoadHandle = reinterpret_cast<EtsNapiOnLoadHandle>(onLoadSymbol.Value());
116 ets_int etsNapiVersion = onLoadHandle(env);
117 if (!napi::CheckVersionEtsNapi(etsNapiVersion)) {
118 return "Unsupported Ets napi version " + std::to_string(etsNapiVersion);
119 }
120 } else if (auto res = lib->FindSymbol("ANI_Constructor")) {
121 using AniCtor = ani_status (*)(ani_vm *, uint32_t *);
122 auto ctor = reinterpret_cast<AniCtor>(res.Value());
123 uint32_t version;
124 ani_vm *vm = PandaEnv::FromEtsEnv(env)->GetEtsVM();
125 ani_status status = ctor(vm, &version);
126 if (status != ANI_OK) {
127 return "ANI_Constructor returns an error: " + std::to_string(status);
128 }
129 if (!ani::IsVersionSupported(version)) {
130 return "Unsupported ANI version: " + std::to_string(version);
131 }
132 } else {
133 // NODE: Return error "Native library doesn't have ANI_Constructor" when ets_napi will be dropped. #22232
134 LOG(WARNING, ANI) << lib->GetName() << " doesn't contain ANI_Constructor";
135 }
136 return {};
137 }
138
GetCallerClassName(EtsEnv * env)139 std::optional<std::string> NativeLibraryProvider::GetCallerClassName(EtsEnv *env)
140 {
141 auto coroutine = PandaEnv::FromEtsEnv(env)->GetEtsCoroutine();
142 if (coroutine == nullptr) {
143 LOG(ERROR, RUNTIME) << "Coroutine is null, failed to get class name.";
144 return std::nullopt;
145 }
146 auto stack = StackWalker::Create(coroutine);
147 if (!stack.HasFrame()) {
148 LOG(ERROR, RUNTIME) << "No valid method in stack, failed to determine class.";
149 return std::nullopt;
150 }
151 auto *method = stack.GetMethod();
152 if (method == nullptr) {
153 LOG(ERROR, RUNTIME) << "Method is null, failed to get class name.";
154 return std::nullopt;
155 }
156 auto *cls = method->GetClass();
157 auto *ctx = cls->GetLoadContext();
158 if (ctx == nullptr || !ctx->IsBootContext()) {
159 LOG(ERROR, RUNTIME) << "load context is not a boot context.";
160 return std::nullopt;
161 }
162 return cls->GetName();
163 }
164
CheckLibraryPermission(EtsEnv * env,const PandaString & fileName)165 bool NativeLibraryProvider::CheckLibraryPermission([[maybe_unused]] EtsEnv *env,
166 [[maybe_unused]] const PandaString &fileName)
167 {
168 #if defined(PANDA_TARGET_OHOS)
169 auto classNameOpt = GetCallerClassName(env);
170 if (!classNameOpt.has_value()) {
171 LOG(ERROR, RUNTIME) << "Failed to retrieve class name during permission check.";
172 return false;
173 }
174 auto className = classNameOpt.value();
175 auto &instance = EtsNamespaceManagerImpl::GetInstance();
176 const auto cb = instance.GetExtensionApiCheckCallback();
177 if (cb == nullptr) {
178 LOG(INFO, RUNTIME) << "ExtensionApiCheckCallback is not registered";
179 return false;
180 }
181 if (!cb(className, fileName.c_str())) {
182 LOG(ERROR, RUNTIME) << "CheckLibraryPermission failed: class name: " << className
183 << " is not in the API allowed list, loading prohibited";
184 return false;
185 }
186 LOG(INFO, RUNTIME) << "NativeLibraryProvider::CheckLibraryPermission: class name detection passed";
187 #endif
188 return true;
189 }
190
ResolveSymbol(const PandaString & name) const191 void *NativeLibraryProvider::ResolveSymbol(const PandaString &name) const
192 {
193 os::memory::ReadLockHolder lock(lock_);
194
195 for (auto &lib : libraries_) {
196 if (auto ptr = lib.FindSymbol(name)) {
197 return ptr.Value();
198 }
199 }
200
201 return nullptr;
202 }
203
GetLibraryPath() const204 PandaVector<PandaString> NativeLibraryProvider::GetLibraryPath() const
205 {
206 os::memory::ReadLockHolder lock(lock_);
207 return libraryPath_;
208 }
209
SetLibraryPath(const PandaVector<PandaString> & pathes)210 void NativeLibraryProvider::SetLibraryPath(const PandaVector<PandaString> &pathes)
211 {
212 os::memory::WriteLockHolder lock(lock_);
213 libraryPath_ = pathes;
214 }
215
AddLibraryPath(const PandaString & path)216 void NativeLibraryProvider::AddLibraryPath(const PandaString &path)
217 {
218 os::memory::WriteLockHolder lock(lock_);
219 libraryPath_.emplace_back(path);
220 }
221
222 } // namespace ark::ets
223