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