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