• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #ifndef ECMASCRIPT_OHOS_OHOS_PKG_ARGS_H
17 #define ECMASCRIPT_OHOS_OHOS_PKG_ARGS_H
18 
19 #include <limits>
20 #include <memory>
21 #include <sstream>
22 #include <string>
23 #include <vector>
24 #include <fcntl.h>
25 
26 #include "ecmascript/base/json_helper.h"
27 #include "ecmascript/base/json_parser.h"
28 #include "ecmascript/compiler/aot_compiler_preprocessor.h"
29 #include "ecmascript/ecma_vm.h"
30 #include "ecmascript/js_array.h"
31 #include "ecmascript/js_handle.h"
32 #include "ecmascript/js_tagged_value.h"
33 #include "ecmascript/jspandafile/js_pandafile_manager.h"
34 #include "ecmascript/log_wrapper.h"
35 #include "ecmascript/log_wrapper.h"
36 #include "ecmascript/mem/c_string.h"
37 #include "ecmascript/module/module_path_helper.h"
38 #include "ecmascript/ohos/enable_aot_list_helper.h"
39 #include "ecmascript/ohos/framework_helper.h"
40 #include "ecmascript/pgo_profiler/pgo_utils.h"
41 #include "ecmascript/platform/file.h"
42 #if defined(CODE_ENCRYPTION_ENABLE)
43 #include "ecmascript/ohos/code_decrypt.h"
44 #endif
45 
46 namespace panda::ecmascript::kungfu {
47 using TransformType = ecmascript::base::JsonHelper::TransformType;
48 class OhosPkgArgs {
49 public:
50     constexpr static const char *const KEY_BUNDLE_NAME = "bundleName";
51     constexpr static const char *const KEY_MODULE_NAME = "moduleName";
52     constexpr static const char *const KEY_PKG_PATH = "pkgPath";
53     constexpr static const char *const KEY_FILE_NAME = "abcName";
54     constexpr static const char *const KEY_ABC_OFFSET = "abcOffset";
55     constexpr static const char *const KEY_ABC_SIZE = "abcSize";
56     constexpr static const char *const KEY_PGO_DIR = "pgoDir";
57     constexpr static const char *const KEY_PROCESS_UID = "processUid";
58     constexpr static const char *const KEY_BUNDLE_UID = "bundleUid";
59     constexpr static const char *const IS_ENCRYPTED_BUNDLE = "isEncryptedBundle";
60     constexpr static const char *const APP_IDENTIFIER = "appIdentifier";
61 
62     OhosPkgArgs() = default;
~OhosPkgArgs()63     ~OhosPkgArgs()
64     {
65         if (GetPkgFd() != -1) {
66             Close(reinterpret_cast<fd_t>(GetPkgFd()));
67             SetPkgFd(-1);
68         }
69     }
70 
ParseArgs(AotCompilerPreprocessor & preProcessor,CompilationOptions & cOptions)71     static bool ParseArgs(AotCompilerPreprocessor &preProcessor, CompilationOptions &cOptions)
72     {
73         ASSERT(preProcessor.runtimeOptions_.IsTargetCompilerMode());
74         std::shared_ptr<OhosPkgArgs> pkgArgs = std::make_shared<OhosPkgArgs>();
75         if (!preProcessor.runtimeOptions_.GetCompilerPkgJsonInfo().empty()) {
76             if (!pkgArgs->ParseFromJson(preProcessor.vm_, preProcessor.runtimeOptions_.GetCompilerPkgJsonInfo())) {
77                 return false;
78             }
79             LOG_COMPILER(INFO) << "Parse main pkg info success.";
80             preProcessor.mainPkgName_ = pkgArgs->GetFullName();
81             preProcessor.pkgsArgs_[preProcessor.mainPkgName_] = pkgArgs;
82             if (!ParseProfilerPath(pkgArgs, preProcessor, cOptions)) {
83                 return false;
84             }
85         }
86         if (preProcessor.runtimeOptions_.GetCompilerEnableExternalPkg() &&
87             !preProcessor.runtimeOptions_.GetCompilerExternalPkgJsonInfo().empty()) {
88             OhosPkgArgs::ParseListFromJson(preProcessor.vm_,
89                                            preProcessor.runtimeOptions_.GetCompilerExternalPkgJsonInfo(),
90                                            preProcessor.pkgsArgs_);
91         }
92         for (const auto &pkgInfo : preProcessor.pkgsArgs_) {
93             preProcessor.pandaFileNames_.emplace_back(pkgInfo.first);
94             pkgInfo.second->Dump();
95         }
96         JSThread *thread = preProcessor.vm_->GetJSThread();
97         FrameworkHelper frameworkHelper(thread);
98         auto &frameworkAbcFiles = frameworkHelper.GetFrameworkAbcFiles();
99         for (const auto &abcPath : frameworkAbcFiles) {
100             preProcessor.pandaFileNames_.emplace_back(abcPath);
101         }
102         return true;
103     }
104 #if defined(CODE_ENCRYPTION_ENABLE)
DecryptSetKey(int fd)105     void DecryptSetKey(int fd) const
106     {
107         if (GetIsEncryptedBundle() <= 0) {
108             return;
109         }
110         if (ohos::DecryptSetKey(fd, static_cast<int>(GetBundleUid())) < 0) {
111             LOG_ECMA(ERROR) << "set key error!";
112         }
113         if (ohos::DecryptAssociateKey(fd, static_cast<int>(GetProcessUid()),
114                                       static_cast<int>(GetBundleUid())) < 0) {
115             LOG_ECMA(ERROR) << "associate key error!";
116         }
117     }
118 
DecryptRemoveKey(int fd)119     void DecryptRemoveKey(int fd) const
120     {
121         if (GetIsEncryptedBundle() <= 0) {
122             return;
123         }
124         if (ohos::DecrypRemoveKey(fd, static_cast<int>(GetProcessUid())) < 0
125          || ohos::DecrypRemoveKey(fd, static_cast<int>(GetBundleUid())) < 0) {
126             LOG_ECMA(ERROR) << "remove key error!";
127         }
128     }
129 
130 #endif
GetJSPandaFileinfo(const JSRuntimeOptions & runtimeOptions,std::string & hapPath,uint32_t & offset,uint32_t & size,std::string & realPath)131     bool GetJSPandaFileinfo(const JSRuntimeOptions &runtimeOptions, std::string &hapPath,
132                             uint32_t &offset, uint32_t &size, std::string &realPath) const
133     {
134         if (Valid()) {
135             hapPath = GetPath();
136             offset = GetOffset();
137             size = GetSize();
138         } else {
139             // for legacy params
140             hapPath = runtimeOptions.GetHapPath();
141             offset = runtimeOptions.GetHapAbcOffset();
142             size = runtimeOptions.GetHapAbcSize();
143         }
144         if (size == 0) {
145             LOG_ECMA(ERROR) << "buffer is empty in target compiler mode!";
146             return false;
147         }
148         if (!RealPath(hapPath, realPath, false)) {
149             LOG_ECMA(ERROR) << "realpath for hap path failed!";
150             return false;
151         }
152         return true;
153     }
154 
GetJSPandaFile(const JSRuntimeOptions & runtimeOptions,std::shared_ptr<JSPandaFile> & pf,int HapVerifyFd)155     bool GetJSPandaFile(const JSRuntimeOptions &runtimeOptions, std::shared_ptr<JSPandaFile> &pf,
156         [[maybe_unused]] int HapVerifyFd) const
157     {
158         std::string hapPath;
159         uint32_t offset {};
160         uint32_t size {};
161         std::string realPath;
162         if (!GetJSPandaFileinfo(runtimeOptions, hapPath, offset, size, realPath)) {
163             return false;
164         }
165 #if defined(CODE_ENCRYPTION_ENABLE)
166         int fd = open(DEV_APP_CRYPTO_PATH, O_RDONLY);
167         FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
168         DecryptSetKey(fd);
169         uint32_t offStart = offset;
170         offStart &= -PAGE_SIZE;
171         MemMap fileMapMem = FileMapForAlignAddressByFd(HapVerifyFd, PAGE_PROT_READ, offset, offStart);
172         offset = offset - offStart;
173         if (fileMapMem.GetOriginAddr() == nullptr) {
174             Close(reinterpret_cast<fd_t>(fd));
175         }
176 #else
177         MemMap fileMapMem = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
178 #endif
179         if (fileMapMem.GetOriginAddr() == nullptr) {
180             LOG_ECMA(ERROR) << "File mmap failed";
181             return false;
182         }
183         uint8_t *buffer = reinterpret_cast<uint8_t *>(fileMapMem.GetOriginAddr()) + offset;
184         JSPandaFileManager *jsPandaFileManager = JSPandaFileManager::GetInstance();
185         pf = jsPandaFileManager->OpenJSPandaFileFromBuffer(buffer, size, GetFullName().c_str());
186         FileUnMap(fileMapMem);
187         fileMapMem.Reset();
188 #if defined(CODE_ENCRYPTION_ENABLE)
189         DecryptRemoveKey(fd);
190         Close(reinterpret_cast<fd_t>(fd));
191 #endif
192         return true;
193     }
194 
ParseListFromJson(EcmaVM * vm,const std::string & jsonInfo,std::map<std::string,std::shared_ptr<OhosPkgArgs>> & argsMap)195     static bool ParseListFromJson(EcmaVM *vm, const std::string &jsonInfo,
196                                   std::map<std::string, std::shared_ptr<OhosPkgArgs>> &argsMap)
197     {
198         LocalScope scope(vm);
199         ObjectFactory *factory = vm->GetFactory();
200         auto *jsThread = vm->GetJSThread();
201         ecmascript::base::Utf8JsonParser parser(jsThread, TransformType::NORMAL);
202 
203         JSHandle<EcmaString> handleStr = factory->NewFromASCII(jsonInfo.c_str());  // JSON Object
204         JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
205         JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
206         if (!resultValue.IsArray(jsThread)) {
207             LOG_COMPILER(ERROR) << "Pkg list info parse failed. result is not an array. jsonData: " << jsonInfo.c_str();
208             return false;
209         }
210         JSHandle<JSArray> valueHandle(jsThread, resultValue);
211         JSHandle<TaggedArray> elements(jsThread, valueHandle->GetElements(jsThread));
212         JSMutableHandle<JSTaggedValue> entry(jsThread, JSTaggedValue::Undefined());
213         JSMutableHandle<JSObject> entryHandle(jsThread, JSTaggedValue::Undefined());
214         for (uint32_t i = 0; i < elements->GetLength(); i++) {
215             entry.Update(elements->Get(jsThread, i));
216             if (entry->IsHole()) {
217                 continue;
218             }
219             std::shared_ptr<OhosPkgArgs> pkgInfo = std::make_shared<OhosPkgArgs>();
220             JSTaggedValue entryValue(static_cast<JSTaggedType>(entry->GetRawData()));
221             entryHandle.Update(entryValue);
222             if (!pkgInfo->ParseFromJsObject(vm, entryHandle)) {
223                 LOG_COMPILER(ERROR) << "Pkg list entry info parse failed. jsonData: " << jsonInfo.c_str();
224                 return false;
225             }
226             argsMap[pkgInfo->GetFullName()] = pkgInfo;
227         }
228         return true;
229     }
230 
ParseFromJson(EcmaVM * vm,const std::string & jsonInfo)231     bool ParseFromJson(EcmaVM *vm, const std::string &jsonInfo)
232     {
233         LocalScope scope(vm);
234         ObjectFactory *factory = vm->GetFactory();
235         auto *jsThread = vm->GetJSThread();
236         ecmascript::base::Utf8JsonParser parser(jsThread, TransformType::NORMAL);
237 
238         JSHandle<EcmaString> handleStr(factory->NewFromASCII(jsonInfo.c_str()));  // JSON Object
239         JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
240         JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
241         if (!resultValue.IsECMAObject()) {
242             LOG_COMPILER(ERROR) << "Pkg info parse failed. result is not an object. jsonData: " << jsonInfo.c_str();
243             return false;
244         }
245         JSHandle<JSObject> valueHandle(jsThread, resultValue);
246         return ParseFromJsObject(vm, valueHandle);
247     }
248 
ParseFromJsObject(EcmaVM * vm,JSHandle<JSObject> & valueHandle)249     bool ParseFromJsObject(EcmaVM *vm, JSHandle<JSObject> &valueHandle)
250     {
251         LocalScope scope(vm);
252         auto *jsThread = vm->GetJSThread();
253         JSHandle<TaggedArray> nameList(JSObject::EnumerableOwnNames(jsThread, valueHandle));
254         JSMutableHandle<JSTaggedValue> key(jsThread, JSTaggedValue::Undefined());
255         JSMutableHandle<JSTaggedValue> value(jsThread, JSTaggedValue::Undefined());
256         for (uint32_t i = 0; i < nameList->GetLength(); i++) {
257             key.Update(nameList->Get(jsThread, i));
258             value.Update(JSObject::GetProperty(jsThread, valueHandle, key).GetValue());
259             if (!key->IsString() || !value->IsString()) {
260                 LOG_COMPILER(ERROR) << "Pkg info parse from js object failed. key and value must be string type.";
261                 return false;
262             }
263             UpdateProperty(ConvertToString(jsThread, *JSTaggedValue::ToString(jsThread, key)).c_str(),
264                            ConvertToString(jsThread, *JSTaggedValue::ToString(jsThread, value)).c_str());
265         }
266         return Valid();
267     }
268 
UpdateProperty(const char * key,const char * value)269     void UpdateProperty(const char *key, const char *value)
270     {
271         if (strcmp(key, KEY_BUNDLE_NAME) == 0) {
272             bundleName_ = value;
273         } else if (strcmp(key, KEY_MODULE_NAME) == 0) {
274             moduleName_ = value;
275         } else if (strcmp(key, KEY_PKG_PATH) == 0) {
276             pkgPath_ = value;
277         } else if (strcmp(key, KEY_FILE_NAME) == 0) {
278             abcName_ = value;
279         } else if (strcmp(key, KEY_ABC_OFFSET) == 0) {
280             char *str = nullptr;
281             abcOffset_ = static_cast<uint32_t>(strtol(value, &str, 0));
282         } else if (strcmp(key, KEY_ABC_SIZE) == 0) {
283             char *str = nullptr;
284             abcSize_ = static_cast<uint32_t>(strtol(value, &str, 0));
285         } else if (strcmp(key, KEY_PGO_DIR) == 0) {
286             pgoDir_ = value;
287         } else if (strcmp(key, KEY_BUNDLE_UID) == 0) {
288             char *str = nullptr;
289             bundleUid_ = static_cast<uint32_t>(strtol(value, &str, 0));
290         } else if (strcmp(key, KEY_PROCESS_UID) == 0) {
291             char *str = nullptr;
292             processUid_ = static_cast<uint32_t>(strtol(value, &str, 0));
293         } else if (strcmp(key, IS_ENCRYPTED_BUNDLE) == 0) {
294             char *str = nullptr;
295             IsEncryptedBundle_ = static_cast<uint32_t>(strtol(value, &str, 0));
296         } else if (strcmp(key, APP_IDENTIFIER) == 0) {
297             appSignature_ = value;
298         } else {
299             LOG_COMPILER(ERROR) << "Unknown keyword when parse pkg info. key: " << key << ", value: " << value;
300         }
301     }
302 
Valid()303     bool Valid() const
304     {
305         if (!base::StringHelper::EndsWith(abcName_, ".abc")) {
306             LOG_COMPILER(ERROR) << KEY_FILE_NAME << " must be abc file, but now is: " << abcName_;
307             return false;
308         }
309         return !bundleName_.empty() && !moduleName_.empty() && !pkgPath_.empty() && (abcOffset_ != INVALID_VALUE) &&
310                (abcSize_ != INVALID_VALUE);
311     }
312 
Dump()313     void Dump() const
314     {
315         LOG_COMPILER(INFO) << "PkgInfo: "
316                            << KEY_BUNDLE_NAME << ": " << bundleName_ << ", "
317                            << KEY_MODULE_NAME << ": " << moduleName_ << ", "
318                            << KEY_PKG_PATH << ": " << pkgPath_ << ", "
319                            << KEY_ABC_OFFSET << ": " << std::hex << abcOffset_ << ", "
320                            << KEY_ABC_SIZE << ": " << abcSize_ << ", "
321                            << KEY_PGO_DIR << ": " << pgoDir_ << ", "
322                            << KEY_BUNDLE_UID << ": " << bundleUid_ << ", "
323                            << KEY_PROCESS_UID << ": " << processUid_ << ", "
324                            << IS_ENCRYPTED_BUNDLE << ": " << IsEncryptedBundle_
325                            << APP_IDENTIFIER << ": " << appSignature_;
326     }
327 
GetBundleName()328     const std::string &GetBundleName() const
329     {
330         return bundleName_;
331     }
332 
GetModuleName()333     const std::string &GetModuleName() const
334     {
335         return moduleName_;
336     }
337 
GetPath()338     const std::string &GetPath() const
339     {
340         return pkgPath_;
341     }
342 
GetAppSignature()343     const std::string &GetAppSignature() const
344     {
345         return appSignature_;
346     }
347 
GetFullName()348     std::string GetFullName() const
349     {
350         return pkgPath_ + GetPathSeparator() + moduleName_ + GetPathSeparator() + abcName_;
351     }
352 
GetOffset()353     uint32_t GetOffset() const
354     {
355         return abcOffset_;
356     }
357 
GetSize()358     uint32_t GetSize() const
359     {
360         return abcSize_;
361     }
362 
GetBundleUid()363     uint32_t GetBundleUid() const
364     {
365         return bundleUid_;
366     }
367 
GetProcessUid()368     uint32_t GetProcessUid() const
369     {
370         return processUid_;
371     }
372 
GetIsEncryptedBundle()373     bool GetIsEncryptedBundle() const
374     {
375         return IsEncryptedBundle_;
376     }
377 
GetPgoDir()378     const std::string &GetPgoDir() const
379     {
380         return pgoDir_;
381     }
382 
SetPgoDir(const std::string & pgoDir)383     void SetPgoDir(const std::string &pgoDir)
384     {
385         pgoDir_ = pgoDir;
386     }
387 
SetPkgFd(int fd)388     void SetPkgFd(int fd)
389     {
390         pkgFd_ = fd;
391     }
392 
GetPkgFd()393     int GetPkgFd() const
394     {
395         return pkgFd_;
396     }
397 
GetPgoPaths(bool isEnableBaselinePgo,std::string & pgoPaths,bool & needMerge)398     void GetPgoPaths(bool isEnableBaselinePgo, std::string &pgoPaths, bool &needMerge) const
399     {
400         // 1. collect runtime ap and merged ap
401         pgoPaths.clear();
402         needMerge = false;
403         pgoPaths = GetTargetApPaths();
404         if (!pgoPaths.empty()) {
405             needMerge = true;
406             return;
407         }
408 
409         // 2. Use the baseline AP if the runtime AP or merge ap do not exist and when install or update the application
410         auto baselineAp = pgoDir_ + '/' + pgo::ApNameUtils::GetOhosPkgApName(moduleName_);
411         if (isEnableBaselinePgo && FileExist(baselineAp.c_str())) {
412             pgoPaths = baselineAp;
413             LOG_COMPILER(DEBUG) << "Do not support base line ap now, please waiting. baseline ap: " << baselineAp;
414         }
415     }
416 
GetRuntimeApPath()417     std::string GetRuntimeApPath() const
418     {
419         auto runtimeAp = pgoDir_ + '/' + pgo::ApNameUtils::GetRuntimeApName(moduleName_);
420         if (!FileExist(runtimeAp.c_str())) {
421             return "";
422         }
423         return runtimeAp;
424     }
425 
GetMergedApPathWithoutCheck()426     std::string GetMergedApPathWithoutCheck() const
427     {
428         return pgoDir_ + '/' + pgo::ApNameUtils::GetMergedApName(moduleName_);
429     }
430 
GetMergedApPath()431     std::string GetMergedApPath() const
432     {
433         auto mergedAp = GetMergedApPathWithoutCheck();
434         if (!FileExist(mergedAp.c_str())) {
435             return "";
436         }
437         return mergedAp;
438     }
439 private:
ParseProfilerPath(std::shared_ptr<OhosPkgArgs> & pkgArgs,AotCompilerPreprocessor & preProcessor,CompilationOptions & cOptions)440     static bool ParseProfilerPath(std::shared_ptr<OhosPkgArgs> &pkgArgs, AotCompilerPreprocessor &preProcessor,
441                                   CompilationOptions &cOptions)
442     {
443         if (!preProcessor.runtimeOptions_.IsPartialCompilerMode()) {
444             return true;
445         }
446         if (pkgArgs->GetPgoDir().empty() && !cOptions.profilerIn_.empty()) {
447             // try get pgo dir from --compiler-pgo-profiler-path
448             arg_list_t apFileNames = base::StringHelper::SplitString(cOptions.profilerIn_, GetFileDelimiter());
449             ASSERT(!apFileNames.empty());
450             // just parse the first ap's dir
451             pkgArgs->SetPgoDir(ResolveDirPath(apFileNames.at(0)));
452         }
453         // reset profilerIn from pgo dir
454         pkgArgs->GetPgoPaths(cOptions.isEnableBaselinePgo_, cOptions.profilerIn_, cOptions.needMerge_);
455         if (cOptions.profilerIn_.empty()) {
456             LOG_COMPILER(WARN) << "No available ap files found in " << pkgArgs->GetPgoDir();
457         }
458         return true;
459     }
460 
461     /*
462     * Before: xxx/xxx
463     * After:  xxx
464     */
ResolveDirPath(const std::string & fileName)465     static std::string ResolveDirPath(const std::string &fileName)
466     {
467         // find last '/', '\\'
468         auto foundPos = fileName.find_last_of("/\\");
469         if (foundPos == std::string::npos) {
470             return "";
471         }
472         return fileName.substr(0, foundPos);
473     }
474 
GetTargetApPaths()475     std::string GetTargetApPaths() const
476     {
477         // handle merged ap
478         std::string pgoPaths = GetMergedApPath();
479 
480         // handle runtime ap
481         auto runtimeAp = GetRuntimeApPath();
482         if (!runtimeAp.empty()) {
483             if (!pgoPaths.empty()) {
484                 pgoPaths += GetFileDelimiter();
485             }
486             pgoPaths += runtimeAp;
487         }
488         return pgoPaths;
489     }
490 
491     static constexpr uint32_t INVALID_VALUE = std::numeric_limits<uint32_t>::max();
492     std::string bundleName_{""};
493     std::string moduleName_{""};
494     std::string pkgPath_{""};
495     std::string abcName_{""};
496     std::string pgoDir_{""};
497     std::string appSignature_{""};
498     uint32_t abcOffset_ {INVALID_VALUE};
499     uint32_t abcSize_ {INVALID_VALUE};
500     uint32_t bundleUid_ {INVALID_VALUE};
501     uint32_t processUid_ {INVALID_VALUE};
502     bool IsEncryptedBundle_{false};
503     int pkgFd_ {-1};
504 };
505 }  // namespace panda::ecmascript::kungfu
506 #endif
507