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