• 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.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(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         DecryptSetKey(fd);
168         uint32_t offStart = offset;
169         offStart &= -PAGE_SIZE;
170         MemMap fileMapMem = FileMapForAlignAddressByFd(HapVerifyFd, PAGE_PROT_READ, offset, offStart);
171         offset = offset - offStart;
172         if (fileMapMem.GetOriginAddr() == nullptr) {
173             close(fd);
174         }
175 #else
176         MemMap fileMapMem = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
177 #endif
178         if (fileMapMem.GetOriginAddr() == nullptr) {
179             LOG_ECMA(ERROR) << "File mmap failed";
180             return false;
181         }
182         uint8_t *buffer = reinterpret_cast<uint8_t *>(fileMapMem.GetOriginAddr()) + offset;
183         JSPandaFileManager *jsPandaFileManager = JSPandaFileManager::GetInstance();
184         pf = jsPandaFileManager->OpenJSPandaFileFromBuffer(buffer, size, GetFullName().c_str());
185         FileUnMap(fileMapMem);
186         fileMapMem.Reset();
187 #if defined(CODE_ENCRYPTION_ENABLE)
188         DecryptRemoveKey(fd);
189         close(fd);
190 #endif
191         return true;
192     }
193 
ParseListFromJson(EcmaVM * vm,const std::string & jsonInfo,std::map<std::string,std::shared_ptr<OhosPkgArgs>> & argsMap)194     static bool ParseListFromJson(EcmaVM *vm, const std::string &jsonInfo,
195                                   std::map<std::string, std::shared_ptr<OhosPkgArgs>> &argsMap)
196     {
197         LocalScope scope(vm);
198         ObjectFactory *factory = vm->GetFactory();
199         auto *jsThread = vm->GetJSThread();
200         ecmascript::base::Utf8JsonParser parser(jsThread, TransformType::NORMAL);
201 
202         JSHandle<EcmaString> handleStr = factory->NewFromASCII(jsonInfo.c_str());  // JSON Object
203         JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
204         JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
205         if (!resultValue.IsArray(jsThread)) {
206             LOG_COMPILER(ERROR) << "Pkg list info parse failed. result is not an array. jsonData: " << jsonInfo.c_str();
207             return false;
208         }
209         JSHandle<JSArray> valueHandle(jsThread, resultValue);
210         JSHandle<TaggedArray> elements(jsThread, valueHandle->GetElements());
211         JSMutableHandle<JSTaggedValue> entry(jsThread, JSTaggedValue::Undefined());
212         JSMutableHandle<JSObject> entryHandle(jsThread, JSTaggedValue::Undefined());
213         for (uint32_t i = 0; i < elements->GetLength(); i++) {
214             entry.Update(elements->Get(i));
215             if (entry->IsHole()) {
216                 continue;
217             }
218             std::shared_ptr<OhosPkgArgs> pkgInfo = std::make_shared<OhosPkgArgs>();
219             JSTaggedValue entryValue(static_cast<JSTaggedType>(entry->GetRawData()));
220             entryHandle.Update(entryValue);
221             if (!pkgInfo->ParseFromJsObject(vm, entryHandle)) {
222                 LOG_COMPILER(ERROR) << "Pkg list entry info parse failed. jsonData: " << jsonInfo.c_str();
223                 return false;
224             }
225             argsMap[pkgInfo->GetFullName()] = pkgInfo;
226         }
227         return true;
228     }
229 
ParseFromJson(EcmaVM * vm,const std::string & jsonInfo)230     bool ParseFromJson(EcmaVM *vm, const std::string &jsonInfo)
231     {
232         LocalScope scope(vm);
233         ObjectFactory *factory = vm->GetFactory();
234         auto *jsThread = vm->GetJSThread();
235         ecmascript::base::Utf8JsonParser parser(jsThread, TransformType::NORMAL);
236 
237         JSHandle<EcmaString> handleStr(factory->NewFromASCII(jsonInfo.c_str()));  // JSON Object
238         JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
239         JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
240         if (!resultValue.IsECMAObject()) {
241             LOG_COMPILER(ERROR) << "Pkg info parse failed. result is not an object. jsonData: " << jsonInfo.c_str();
242             return false;
243         }
244         JSHandle<JSObject> valueHandle(jsThread, resultValue);
245         return ParseFromJsObject(vm, valueHandle);
246     }
247 
ParseFromJsObject(EcmaVM * vm,JSHandle<JSObject> & valueHandle)248     bool ParseFromJsObject(EcmaVM *vm, JSHandle<JSObject> &valueHandle)
249     {
250         LocalScope scope(vm);
251         auto *jsThread = vm->GetJSThread();
252         JSHandle<TaggedArray> nameList(JSObject::EnumerableOwnNames(jsThread, valueHandle));
253         JSMutableHandle<JSTaggedValue> key(jsThread, JSTaggedValue::Undefined());
254         JSMutableHandle<JSTaggedValue> value(jsThread, JSTaggedValue::Undefined());
255         for (uint32_t i = 0; i < nameList->GetLength(); i++) {
256             key.Update(nameList->Get(i));
257             value.Update(JSObject::GetProperty(jsThread, valueHandle, key).GetValue());
258             if (!key->IsString() || !value->IsString()) {
259                 LOG_COMPILER(ERROR) << "Pkg info parse from js object failed. key and value must be string type.";
260                 return false;
261             }
262             UpdateProperty(ConvertToString(*JSTaggedValue::ToString(jsThread, key)).c_str(),
263                            ConvertToString(*JSTaggedValue::ToString(jsThread, value)).c_str());
264         }
265         return Valid();
266     }
267 
UpdateProperty(const char * key,const char * value)268     void UpdateProperty(const char *key, const char *value)
269     {
270         if (strcmp(key, KEY_BUNDLE_NAME) == 0) {
271             bundleName_ = value;
272         } else if (strcmp(key, KEY_MODULE_NAME) == 0) {
273             moduleName_ = value;
274         } else if (strcmp(key, KEY_PKG_PATH) == 0) {
275             pkgPath_ = value;
276         } else if (strcmp(key, KEY_FILE_NAME) == 0) {
277             abcName_ = value;
278         } else if (strcmp(key, KEY_ABC_OFFSET) == 0) {
279             char *str = nullptr;
280             abcOffset_ = static_cast<uint32_t>(strtol(value, &str, 0));
281         } else if (strcmp(key, KEY_ABC_SIZE) == 0) {
282             char *str = nullptr;
283             abcSize_ = static_cast<uint32_t>(strtol(value, &str, 0));
284         } else if (strcmp(key, KEY_PGO_DIR) == 0) {
285             pgoDir_ = value;
286         } else if (strcmp(key, KEY_BUNDLE_UID) == 0) {
287             char *str = nullptr;
288             bundleUid_ = static_cast<uint32_t>(strtol(value, &str, 0));
289         } else if (strcmp(key, KEY_PROCESS_UID) == 0) {
290             char *str = nullptr;
291             processUid_ = static_cast<uint32_t>(strtol(value, &str, 0));
292         } else if (strcmp(key, IS_ENCRYPTED_BUNDLE) == 0) {
293             char *str = nullptr;
294             IsEncryptedBundle_ = static_cast<uint32_t>(strtol(value, &str, 0));
295         } else if (strcmp(key, APP_IDENTIFIER) == 0) {
296             appSignature_ = value;
297         } else {
298             LOG_COMPILER(ERROR) << "Unknown keyword when parse pkg info. key: " << key << ", value: " << value;
299         }
300     }
301 
Valid()302     bool Valid() const
303     {
304         if (!base::StringHelper::EndsWith(abcName_, ".abc")) {
305             LOG_COMPILER(ERROR) << KEY_FILE_NAME << " must be abc file, but now is: " << abcName_;
306             return false;
307         }
308         return !bundleName_.empty() && !moduleName_.empty() && !pkgPath_.empty() && (abcOffset_ != INVALID_VALUE) &&
309                (abcSize_ != INVALID_VALUE);
310     }
311 
Dump()312     void Dump() const
313     {
314         LOG_COMPILER(INFO) << "PkgInfo: "
315                            << KEY_BUNDLE_NAME << ": " << bundleName_ << ", "
316                            << KEY_MODULE_NAME << ": " << moduleName_ << ", "
317                            << KEY_PKG_PATH << ": " << pkgPath_ << ", "
318                            << KEY_ABC_OFFSET << ": " << std::hex << abcOffset_ << ", "
319                            << KEY_ABC_SIZE << ": " << abcSize_ << ", "
320                            << KEY_PGO_DIR << ": " << pgoDir_ << ", "
321                            << KEY_BUNDLE_UID << ": " << bundleUid_ << ", "
322                            << KEY_PROCESS_UID << ": " << processUid_ << ", "
323                            << IS_ENCRYPTED_BUNDLE << ": " << IsEncryptedBundle_
324                            << APP_IDENTIFIER << ": " << appSignature_;
325     }
326 
GetBundleName()327     const std::string &GetBundleName() const
328     {
329         return bundleName_;
330     }
331 
GetModuleName()332     const std::string &GetModuleName() const
333     {
334         return moduleName_;
335     }
336 
GetPath()337     const std::string &GetPath() const
338     {
339         return pkgPath_;
340     }
341 
GetAppSignature()342     const std::string &GetAppSignature() const
343     {
344         return appSignature_;
345     }
346 
GetFullName()347     std::string GetFullName() const
348     {
349         return pkgPath_ + GetPathSeparator() + moduleName_ + GetPathSeparator() + abcName_;
350     }
351 
GetOffset()352     uint32_t GetOffset() const
353     {
354         return abcOffset_;
355     }
356 
GetSize()357     uint32_t GetSize() const
358     {
359         return abcSize_;
360     }
361 
GetBundleUid()362     uint32_t GetBundleUid() const
363     {
364         return bundleUid_;
365     }
366 
GetProcessUid()367     uint32_t GetProcessUid() const
368     {
369         return processUid_;
370     }
371 
GetIsEncryptedBundle()372     bool GetIsEncryptedBundle() const
373     {
374         return IsEncryptedBundle_;
375     }
376 
GetPgoDir()377     const std::string &GetPgoDir() const
378     {
379         return pgoDir_;
380     }
381 
SetPgoDir(const std::string & pgoDir)382     void SetPgoDir(const std::string &pgoDir)
383     {
384         pgoDir_ = pgoDir;
385     }
386 
SetPkgFd(int fd)387     void SetPkgFd(int fd)
388     {
389         pkgFd_ = fd;
390     }
391 
GetPkgFd()392     int GetPkgFd() const
393     {
394         return pkgFd_;
395     }
396 
GetPgoPaths(bool isEnableBaselinePgo,std::string & pgoPaths,bool & needMerge)397     void GetPgoPaths(bool isEnableBaselinePgo, std::string &pgoPaths, bool &needMerge) const
398     {
399         // 1. collect runtime ap and merged ap
400         pgoPaths.clear();
401         needMerge = false;
402         pgoPaths = GetTargetApPaths();
403         if (!pgoPaths.empty()) {
404             needMerge = true;
405             return;
406         }
407 
408         // 2. Use the baseline AP if the runtime AP or merge ap do not exist and when install or update the application
409         auto baselineAp = pgoDir_ + '/' + pgo::ApNameUtils::GetOhosPkgApName(moduleName_);
410         if (isEnableBaselinePgo && FileExist(baselineAp.c_str())) {
411             pgoPaths = baselineAp;
412             LOG_COMPILER(DEBUG) << "Do not support base line ap now, please waiting. baseline ap: " << baselineAp;
413         }
414     }
415 
GetRuntimeApPath()416     std::string GetRuntimeApPath() const
417     {
418         auto runtimeAp = pgoDir_ + '/' + pgo::ApNameUtils::GetRuntimeApName(moduleName_);
419         if (!FileExist(runtimeAp.c_str())) {
420             return "";
421         }
422         return runtimeAp;
423     }
424 
GetMergedApPathWithoutCheck()425     std::string GetMergedApPathWithoutCheck() const
426     {
427         return pgoDir_ + '/' + pgo::ApNameUtils::GetMergedApName(moduleName_);
428     }
429 
GetMergedApPath()430     std::string GetMergedApPath() const
431     {
432         auto mergedAp = GetMergedApPathWithoutCheck();
433         if (!FileExist(mergedAp.c_str())) {
434             return "";
435         }
436         return mergedAp;
437     }
438 private:
ParseProfilerPath(std::shared_ptr<OhosPkgArgs> & pkgArgs,AotCompilerPreprocessor & preProcessor,CompilationOptions & cOptions)439     static bool ParseProfilerPath(std::shared_ptr<OhosPkgArgs> &pkgArgs, AotCompilerPreprocessor &preProcessor,
440                                   CompilationOptions &cOptions)
441     {
442         if (!preProcessor.runtimeOptions_.IsPartialCompilerMode()) {
443             return true;
444         }
445         if (pkgArgs->GetPgoDir().empty() && !cOptions.profilerIn_.empty()) {
446             // try get pgo dir from --compiler-pgo-profiler-path
447             arg_list_t apFileNames = base::StringHelper::SplitString(cOptions.profilerIn_, GetFileDelimiter());
448             ASSERT(!apFileNames.empty());
449             // just parse the first ap's dir
450             pkgArgs->SetPgoDir(ResolveDirPath(apFileNames.at(0)));
451         }
452         // reset profilerIn from pgo dir
453         pkgArgs->GetPgoPaths(cOptions.isEnableBaselinePgo_, cOptions.profilerIn_, cOptions.needMerge_);
454         if (cOptions.profilerIn_.empty()) {
455             LOG_COMPILER(WARN) << "No available ap files found in " << pkgArgs->GetPgoDir();
456         }
457         return true;
458     }
459 
460     /*
461     * Before: xxx/xxx
462     * After:  xxx
463     */
ResolveDirPath(const std::string & fileName)464     static std::string ResolveDirPath(const std::string &fileName)
465     {
466         // find last '/', '\\'
467         auto foundPos = fileName.find_last_of("/\\");
468         if (foundPos == std::string::npos) {
469             return "";
470         }
471         return fileName.substr(0, foundPos);
472     }
473 
GetTargetApPaths()474     std::string GetTargetApPaths() const
475     {
476         // handle merged ap
477         std::string pgoPaths = GetMergedApPath();
478 
479         // handle runtime ap
480         auto runtimeAp = GetRuntimeApPath();
481         if (!runtimeAp.empty()) {
482             if (!pgoPaths.empty()) {
483                 pgoPaths += GetFileDelimiter();
484             }
485             pgoPaths += runtimeAp;
486         }
487         return pgoPaths;
488     }
489 
490     static constexpr uint32_t INVALID_VALUE = std::numeric_limits<uint32_t>::max();
491     std::string bundleName_{""};
492     std::string moduleName_{""};
493     std::string pkgPath_{""};
494     std::string abcName_{""};
495     std::string pgoDir_{""};
496     std::string appSignature_{""};
497     uint32_t abcOffset_ {INVALID_VALUE};
498     uint32_t abcSize_ {INVALID_VALUE};
499     uint32_t bundleUid_ {INVALID_VALUE};
500     uint32_t processUid_ {INVALID_VALUE};
501     bool IsEncryptedBundle_{false};
502     int pkgFd_ {-1};
503 };
504 }  // namespace panda::ecmascript::kungfu
505 #endif
506