• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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