1 /*
2 * Copyright (c) 2021-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 #include "ecmascript/compiler/aot_file/aot_file_manager.h"
16
17 #include "ecmascript/base/config.h"
18 #include "ecmascript/base/file_header.h"
19 #include "ecmascript/compiler/aot_file/an_file_data_manager.h"
20 #include "ecmascript/compiler/aot_file/elf_builder.h"
21 #include "ecmascript/compiler/aot_file/elf_reader.h"
22 #include "ecmascript/compiler/bc_call_signature.h"
23 #include "ecmascript/compiler/common_stubs.h"
24 #include "ecmascript/compiler/compiler_log.h"
25 #include "ecmascript/deoptimizer/deoptimizer.h"
26 #include "ecmascript/ecma_vm.h"
27 #include "ecmascript/js_file_path.h"
28 #include "ecmascript/js_runtime_options.h"
29 #include "ecmascript/js_thread.h"
30 #include "ecmascript/jspandafile/constpool_value.h"
31 #include "ecmascript/jspandafile/js_pandafile.h"
32 #include "ecmascript/jspandafile/program_object.h"
33 #include "ecmascript/mem/region.h"
34 #include "ecmascript/message_string.h"
35 #include "ecmascript/snapshot/mem/snapshot.h"
36 #include "ecmascript/stackmap/ark_stackmap_parser.h"
37 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
38
39 namespace panda::ecmascript {
40 using CommonStubCSigns = kungfu::CommonStubCSigns;
41 using BytecodeStubCSigns = kungfu::BytecodeStubCSigns;
42
Iterate(const RootVisitor & v)43 void AOTFileManager::Iterate(const RootVisitor &v)
44 {
45 for (auto &iter : desCPs_) {
46 for (auto &curCP : iter.second) {
47 v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&iter.second.at(curCP.first))));
48 }
49 }
50 }
51
DumpAOTInfo()52 void AOTFileManager::DumpAOTInfo()
53 {
54 AnFileDataManager *m = AnFileDataManager::GetInstance();
55 m->Dump();
56 }
57
LoadStubFile(const std::string & fileName)58 void AOTFileManager::LoadStubFile(const std::string &fileName)
59 {
60 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
61 if (!anFileDataManager->SafeLoad(fileName, AnFileDataManager::Type::STUB)) {
62 return;
63 }
64 auto info = anFileDataManager->SafeGetStubFileInfo();
65 auto stubs = info->GetStubs();
66 InitializeStubEntries(stubs);
67 }
68
LoadAnFile(const std::string & fileName)69 bool AOTFileManager::LoadAnFile(const std::string &fileName)
70 {
71 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
72 return anFileDataManager->SafeLoad(fileName, AnFileDataManager::Type::AOT);
73 }
74
LoadAiFile(const std::string & filename)75 bool AOTFileManager::LoadAiFile([[maybe_unused]] const std::string &filename)
76 {
77 Snapshot snapshot(vm_);
78 #if !WIN_OR_MAC_OR_IOS_PLATFORM
79 return snapshot.Deserialize(SnapshotType::AI, filename.c_str());
80 #else
81 return true;
82 #endif
83 }
84
LoadAiFile(const JSPandaFile * jsPandaFile)85 void AOTFileManager::LoadAiFile(const JSPandaFile *jsPandaFile)
86 {
87 uint32_t anFileInfoIndex = GetAnFileIndex(jsPandaFile);
88 // this abc file does not have corresponding an file
89 if (anFileInfoIndex == INVALID_INDEX) {
90 return;
91 }
92
93 auto iter = desCPs_.find(anFileInfoIndex);
94 // already loaded
95 if (iter != desCPs_.end()) {
96 return;
97 }
98
99 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
100 std::string aiFilename = anFileDataManager->GetDir();
101 aiFilename += JSFilePath::GetHapName(jsPandaFile) + AOTFileManager::FILE_EXTENSION_AI;
102 LoadAiFile(aiFilename);
103 }
104
GetAnFileInfo(const JSPandaFile * jsPandaFile) const105 const std::shared_ptr<AnFileInfo> AOTFileManager::GetAnFileInfo(const JSPandaFile *jsPandaFile) const
106 {
107 uint32_t index = GetAnFileIndex(jsPandaFile);
108 if (index == INVALID_INDEX) {
109 return nullptr;
110 }
111 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
112 return anFileDataManager->SafeGetAnFileInfo(index);
113 }
114
IsLoad(const JSPandaFile * jsPandaFile) const115 bool AOTFileManager::IsLoad(const JSPandaFile *jsPandaFile) const
116 {
117 if (!AnFileDataManager::GetInstance()->IsEnable()) {
118 return false;
119 }
120
121 const std::shared_ptr<AnFileInfo> anFileInfo = GetAnFileInfo(jsPandaFile);
122 if (anFileInfo == nullptr) {
123 return false;
124 }
125 return anFileInfo->IsLoad();
126 }
127
IsLoadMain(const JSPandaFile * jsPandaFile,const CString & entry) const128 bool AOTFileManager::IsLoadMain(const JSPandaFile *jsPandaFile, const CString &entry) const
129 {
130 if (!jsPandaFile->IsLoadedAOT()) {
131 return false;
132 }
133
134 const std::shared_ptr<AnFileInfo> anFileInfo = GetAnFileInfo(jsPandaFile);
135 if (anFileInfo == nullptr) {
136 return false;
137 }
138
139 return anFileInfo->IsLoadMain(jsPandaFile, entry);
140 }
141
GetAnFileIndex(const JSPandaFile * jsPandaFile) const142 uint32_t AOTFileManager::GetAnFileIndex(const JSPandaFile *jsPandaFile) const
143 {
144 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
145
146 // run via command line
147 if (vm_->GetJSOptions().WasAOTOutputFileSet()) {
148 std::string jsPandaFileDesc = jsPandaFile->GetJSPandaFileDesc().c_str();
149 std::string baseName = JSFilePath::GetFileName(jsPandaFileDesc);
150 if (baseName.empty()) {
151 return INVALID_INDEX;
152 }
153 std::string anFileName = baseName + FILE_EXTENSION_AN;
154 return anFileDataManager->SafeGetFileInfoIndex(anFileName);
155 }
156
157 // run from app hap
158 std::string hapName = JSFilePath::GetHapName(jsPandaFile);
159 if (hapName.empty()) {
160 return INVALID_INDEX;
161 }
162 std::string anFileName = hapName + FILE_EXTENSION_AN;
163 return anFileDataManager->SafeGetFileInfoIndex(anFileName);
164 }
165
TryReadLock()166 bool AOTFileManager::TryReadLock()
167 {
168 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
169 return anFileDataManager->SafeTryReadLock();
170 }
171
InsideStub(uintptr_t pc)172 bool AOTFileManager::InsideStub(uintptr_t pc)
173 {
174 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
175 return anFileDataManager->SafeInsideStub(pc);
176 }
177
InsideAOT(uintptr_t pc)178 bool AOTFileManager::InsideAOT(uintptr_t pc)
179 {
180 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
181 return anFileDataManager->SafeInsideAOT(pc);
182 }
183
CalCallSiteInfo(uintptr_t retAddr)184 AOTFileInfo::CallSiteInfo AOTFileManager::CalCallSiteInfo(uintptr_t retAddr)
185 {
186 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
187 return anFileDataManager->SafeCalCallSiteInfo(retAddr);
188 }
189
PrintAOTEntry(const JSPandaFile * file,const Method * method,uintptr_t entry)190 void AOTFileManager::PrintAOTEntry(const JSPandaFile *file, const Method *method, uintptr_t entry)
191 {
192 uint32_t mId = method->GetMethodId().GetOffset();
193 std::string mName = method->GetMethodName(file);
194 std::string fileName = file->GetFileName();
195 LOG_COMPILER(INFO) << "Bind " << mName << "@" << mId << "@" << fileName
196 << " -> AOT-Entry = " << reinterpret_cast<void *>(entry);
197 }
198
SetAOTMainFuncEntry(JSHandle<JSFunction> mainFunc,const JSPandaFile * jsPandaFile,std::string_view entryPoint)199 void AOTFileManager::SetAOTMainFuncEntry(JSHandle<JSFunction> mainFunc, const JSPandaFile *jsPandaFile,
200 std::string_view entryPoint)
201 {
202 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
203 uint32_t anFileInfoIndex = jsPandaFile->GetAOTFileInfoIndex();
204 const std::shared_ptr<AnFileInfo> anFileInfo = anFileDataManager->SafeGetAnFileInfo(anFileInfoIndex);
205 // get main func method
206 auto mainFuncMethodId = jsPandaFile->GetMainMethodIndex(entryPoint.data());
207 uint64_t mainEntry;
208 bool isFastCall;
209 std::tie(mainEntry, isFastCall) = anFileInfo->GetMainFuncEntry(mainFuncMethodId);
210 MethodLiteral *mainMethod = jsPandaFile->FindMethodLiteral(mainFuncMethodId);
211 mainMethod->SetAotCodeBit(true);
212 mainMethod->SetNativeBit(false);
213 Method *method = mainFunc->GetCallTarget();
214 method->SetDeoptThreshold(vm_->GetJSOptions().GetDeoptThreshold());
215 method->SetCodeEntryAndMarkAOT(static_cast<uintptr_t>(mainEntry));
216 method->SetIsFastCall(isFastCall);
217 #ifndef NDEBUG
218 PrintAOTEntry(jsPandaFile, method, mainEntry);
219 #endif
220 }
221
SetAOTFuncEntry(const JSPandaFile * jsPandaFile,Method * method,uint32_t entryIndex,bool * canFastCall)222 void AOTFileManager::SetAOTFuncEntry(const JSPandaFile *jsPandaFile, Method *method,
223 uint32_t entryIndex, bool *canFastCall)
224 {
225 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
226 uint32_t anFileInfoIndex = jsPandaFile->GetAOTFileInfoIndex();
227 const std::shared_ptr<AnFileInfo> anFileInfo = anFileDataManager->SafeGetAnFileInfo(anFileInfoIndex);
228 const AOTFileInfo::FuncEntryDes &entry = anFileInfo->GetStubDes(entryIndex);
229 uint64_t codeEntry = entry.codeAddr_;
230 #ifndef NDEBUG
231 PrintAOTEntry(jsPandaFile, method, codeEntry);
232 #endif
233 if (!codeEntry) {
234 return;
235 }
236 method->SetDeoptThreshold(vm_->GetJSOptions().GetDeoptThreshold());
237 method->SetCodeEntryAndMarkAOT(codeEntry);
238 method->SetIsFastCall(entry.isFastCall_);
239 if (canFastCall != nullptr) {
240 *canFastCall = entry.isFastCall_;
241 }
242 }
243
GetStackMapParser() const244 kungfu::ArkStackMapParser *AOTFileManager::GetStackMapParser() const
245 {
246 return arkStackMapParser_;
247 }
248
AdjustBCStubAndDebuggerStubEntries(JSThread * thread,const std::vector<AOTFileInfo::FuncEntryDes> & stubs,const AsmInterParsedOption & asmInterOpt)249 void AOTFileManager::AdjustBCStubAndDebuggerStubEntries(JSThread *thread,
250 const std::vector<AOTFileInfo::FuncEntryDes> &stubs,
251 const AsmInterParsedOption &asmInterOpt)
252 {
253 auto defaultBCStubDes = stubs[BytecodeStubCSigns::SingleStepDebugging];
254 auto defaultBCDebuggerStubDes = stubs[BytecodeStubCSigns::BCDebuggerEntry];
255 auto defaultBCDebuggerExceptionStubDes = stubs[BytecodeStubCSigns::BCDebuggerExceptionEntry];
256 ASSERT(defaultBCStubDes.kind_ == CallSignature::TargetKind::BYTECODE_HELPER_HANDLER);
257 if (asmInterOpt.handleStart >= 0 && asmInterOpt.handleStart <= asmInterOpt.handleEnd) {
258 for (int i = asmInterOpt.handleStart; i <= asmInterOpt.handleEnd; i++) {
259 thread->SetBCStubEntry(static_cast<size_t>(i), defaultBCStubDes.codeAddr_);
260 }
261 #define DISABLE_SINGLE_STEP_DEBUGGING(name) \
262 thread->SetBCStubEntry(BytecodeStubCSigns::ID_##name, stubs[BytecodeStubCSigns::ID_##name].codeAddr_);
263 INTERPRETER_DISABLE_SINGLE_STEP_DEBUGGING_BC_STUB_LIST(DISABLE_SINGLE_STEP_DEBUGGING)
264 #undef DISABLE_SINGLE_STEP_DEBUGGING
265 }
266 for (size_t i = 0; i < BCStubEntries::EXISTING_BC_HANDLER_STUB_ENTRIES_COUNT; i++) {
267 if (i == BytecodeStubCSigns::ID_ExceptionHandler) {
268 thread->SetBCDebugStubEntry(i, defaultBCDebuggerExceptionStubDes.codeAddr_);
269 continue;
270 }
271 thread->SetBCDebugStubEntry(i, defaultBCDebuggerStubDes.codeAddr_);
272 }
273 }
274
InitializeStubEntries(const std::vector<AnFileInfo::FuncEntryDes> & stubs)275 void AOTFileManager::InitializeStubEntries(const std::vector<AnFileInfo::FuncEntryDes> &stubs)
276 {
277 auto thread = vm_->GetAssociatedJSThread();
278 size_t len = stubs.size();
279 for (size_t i = 0; i < len; i++) {
280 auto des = stubs[i];
281 if (des.IsCommonStub()) {
282 thread->SetFastStubEntry(des.indexInKindOrMethodId_, des.codeAddr_);
283 } else if (des.IsBCStub()) {
284 thread->SetBCStubEntry(des.indexInKindOrMethodId_, des.codeAddr_);
285 #if ECMASCRIPT_ENABLE_ASM_FILE_LOAD_LOG
286 auto start = GET_MESSAGE_STRING_ID(HandleLdundefined);
287 std::string format = MessageString::GetMessageString(des.indexInKindOrMethodId_ + start);
288 LOG_ECMA(DEBUG) << "bytecode index: " << des.indexInKindOrMethodId_ << " :" << format << " addr: 0x"
289 << std::hex << des.codeAddr_;
290 #endif
291 } else if (des.IsBuiltinsStub()) {
292 thread->SetBuiltinStubEntry(des.indexInKindOrMethodId_, des.codeAddr_);
293 #if ECMASCRIPT_ENABLE_ASM_FILE_LOAD_LOG
294 int start = GET_MESSAGE_STRING_ID(CharCodeAt);
295 std::string format = MessageString::GetMessageString(des.indexInKindOrMethodId_ + start - 1); // -1: NONE
296 LOG_ECMA(DEBUG) << "builtins index: " << std::dec << des.indexInKindOrMethodId_ << " :" << format
297 << " addr: 0x" << std::hex << des.codeAddr_;
298 #endif
299 } else {
300 thread->RegisterRTInterface(des.indexInKindOrMethodId_, des.codeAddr_);
301 #if ECMASCRIPT_ENABLE_ASM_FILE_LOAD_LOG
302 int start = GET_MESSAGE_STRING_ID(CallRuntime);
303 std::string format = MessageString::GetMessageString(des.indexInKindOrMethodId_ + start);
304 LOG_ECMA(DEBUG) << "runtime index: " << std::dec << des.indexInKindOrMethodId_ << " :" << format
305 << " addr: 0x" << std::hex << des.codeAddr_;
306 #endif
307 }
308 }
309 thread->CheckOrSwitchPGOStubs();
310 AsmInterParsedOption asmInterOpt = vm_->GetJSOptions().GetAsmInterParsedOption();
311 AdjustBCStubAndDebuggerStubEntries(thread, stubs, asmInterOpt);
312 }
313
RewriteDataSection(uintptr_t dataSec,size_t size,uintptr_t newData,size_t newSize)314 bool AOTFileManager::RewriteDataSection(uintptr_t dataSec, size_t size, uintptr_t newData, size_t newSize)
315 {
316 if (memcpy_s(reinterpret_cast<void *>(dataSec), size, reinterpret_cast<void *>(newData), newSize) != EOK) {
317 LOG_FULL(FATAL) << "memset failed";
318 return false;
319 }
320 return true;
321 }
322
AddConstantPool(const CString & snapshotFileName,JSTaggedValue deserializedCPList)323 void AOTFileManager::AddConstantPool(const CString &snapshotFileName, JSTaggedValue deserializedCPList)
324 {
325 AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance();
326 std::string baseName = JSFilePath::GetFileName(snapshotFileName.c_str());
327 uint32_t anFileInfoIndex = anFileDataManager->SafeGetFileInfoIndex(baseName + FILE_EXTENSION_AN);
328
329 desCPs_.insert({anFileInfoIndex, CMap<int32_t, JSTaggedValue> {}});
330 CMap<int32_t, JSTaggedValue> &cpMap = desCPs_[anFileInfoIndex];
331
332 JSHandle<TaggedArray> cpList(vm_->GetJSThread(), deserializedCPList);
333 uint32_t len = cpList->GetLength();
334 for (uint32_t pos = 0; pos < len; pos += DESERI_CP_ITEM_SIZE) {
335 int32_t constantPoolID = cpList->Get(pos).GetInt();
336 JSTaggedValue cp = cpList->Get(pos + 1);
337 cpMap.insert({constantPoolID, cp});
338 }
339 }
340
GetDeserializedConstantPool(const JSPandaFile * jsPandaFile,int32_t cpID)341 JSHandle<JSTaggedValue> AOTFileManager::GetDeserializedConstantPool(const JSPandaFile *jsPandaFile, int32_t cpID)
342 {
343 // The deserialization of the 'ai' data used by the multi-work
344 // is not implemented yet, so there may be a case where
345 // desCPs_ is empty, in which case the Hole will be returned
346 if (desCPs_.empty()) {
347 return JSHandle<JSTaggedValue>(vm_->GetJSThread(), JSTaggedValue::Hole());
348 }
349 uint32_t anFileInfoIndex = jsPandaFile->GetAOTFileInfoIndex();
350 CMap<int32_t, JSTaggedValue> &cpMap = desCPs_.at(anFileInfoIndex);
351 auto iter = cpMap.find(cpID);
352 if (iter == cpMap.end()) {
353 LOG_COMPILER(FATAL) << "can not find deserialized constantpool in anFileInfo, constantPoolID is " << cpID;
354 UNREACHABLE();
355 }
356 return JSHandle<JSTaggedValue>(uintptr_t(&iter->second));
357 }
358
~AOTFileManager()359 AOTFileManager::~AOTFileManager()
360 {
361 if (arkStackMapParser_ != nullptr) {
362 delete arkStackMapParser_;
363 arkStackMapParser_ = nullptr;
364 }
365 }
366
AOTFileManager(EcmaVM * vm)367 AOTFileManager::AOTFileManager(EcmaVM *vm) : vm_(vm), factory_(vm->GetFactory())
368 {
369 bool enableLog = vm->GetJSOptions().WasSetCompilerLogOption();
370 arkStackMapParser_ = new kungfu::ArkStackMapParser(enableLog);
371 }
372
GetAbsolutePath(JSThread * thread,JSTaggedValue relativePathVal)373 JSTaggedValue AOTFileManager::GetAbsolutePath(JSThread *thread, JSTaggedValue relativePathVal)
374 {
375 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
376 CString relativePath = ConvertToString(relativePathVal);
377 CString absPath;
378 if (!GetAbsolutePath(relativePath, absPath)) {
379 LOG_FULL(FATAL) << "Get Absolute Path failed";
380 return JSTaggedValue::Hole();
381 }
382 JSTaggedValue absPathVal = factory->NewFromUtf8(absPath).GetTaggedValue();
383 return absPathVal;
384 }
385
GetAbsolutePath(const CString & relativePathCstr,CString & absPathCstr)386 bool AOTFileManager::GetAbsolutePath(const CString &relativePathCstr, CString &absPathCstr)
387 {
388 std::string relativePath = ConvertToStdString(relativePathCstr);
389 std::string absPath;
390 if (RealPath(relativePath, absPath)) {
391 absPathCstr = ConvertToString(absPath);
392 return true;
393 }
394 return false;
395 }
396
GetHeap()397 const Heap *AOTFileManager::GetHeap()
398 {
399 if (vm_ == nullptr) {
400 return nullptr;
401 }
402 return vm_->GetHeap();
403 }
404 } // namespace panda::ecmascript
405