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