1 /*
2 * Copyright (c) 2023-2024 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 #include "ecmascript/mem/machine_code.h"
17 #include "ecmascript/base/config.h"
18 #include "ecmascript/compiler/aot_file/func_entry_des.h"
19 #include "ecmascript/jit/jit.h"
20 #include "ecmascript/compiler/jit_compilation_env.h"
21 #if ECMASCRIPT_ENABLE_CAST_CHECK
22 #include "ecmascript/js_tagged_value-inl.h"
23 #endif
24
25 namespace panda::ecmascript {
26
SetPageProtect(uint8_t * textStart,size_t dataSize)27 static bool SetPageProtect(uint8_t *textStart, size_t dataSize)
28 {
29 if (!Jit::GetInstance()->IsEnableJitFort()) {
30 constexpr size_t pageSize = 4096;
31 uintptr_t startPage = AlignDown(reinterpret_cast<uintptr_t>(textStart), pageSize);
32 uintptr_t endPage = AlignUp(reinterpret_cast<uintptr_t>(textStart) + dataSize, pageSize);
33 size_t protSize = endPage - startPage;
34 return PageProtect(reinterpret_cast<void*>(startPage), protSize, PAGE_PROT_EXEC_READWRITE);
35 }
36 return true;
37 }
38
MachineCodeCopyToCache(const MachineCodeDesc & desc,uint8_t * pText)39 static int MachineCodeCopyToCache([[maybe_unused]] const MachineCodeDesc &desc, [[maybe_unused]] uint8_t *pText)
40 {
41 #ifdef JIT_ENABLE_CODE_SIGN
42 if ((uintptr_t)desc.codeSigner == 0) {
43 if (memcpy_s(pText, desc.codeSizeAlign, reinterpret_cast<uint8_t*>(desc.codeAddr), desc.codeSize) != EOK) {
44 LOG_JIT(ERROR) << "memcpy failed in CopyToCache";
45 return false;
46 }
47 } else {
48 LOG_JIT(DEBUG) << "Copy: " << std::hex << (uintptr_t)pText << " <- " << std::hex << (uintptr_t)desc.codeAddr
49 << " size: " << desc.codeSize;
50 LOG_JIT(DEBUG) << " codeSigner = " << std::hex << (uintptr_t)desc.codeSigner;
51 OHOS::Security::CodeSign::JitCodeSigner* signer =
52 reinterpret_cast<OHOS::Security::CodeSign::JitCodeSigner*>(desc.codeSigner);
53 int err = OHOS::Security::CodeSign::CopyToJitCode(signer, pText, reinterpret_cast<void*>(desc.codeAddr),
54 desc.codeSize);
55 if (err != EOK) {
56 LOG_JIT(ERROR) << " CopyToJitCode failed, err: " << err;
57 return false;
58 } else {
59 LOG_JIT(DEBUG) << " CopyToJitCode success!!";
60 }
61 delete reinterpret_cast<OHOS::Security::CodeSign::JitCodeSigner*>(desc.codeSigner);
62 }
63 #else
64 if (memcpy_s(pText, desc.codeSizeAlign, // LCOV_EXCL_BR_LINE
65 reinterpret_cast<uint8_t*>(desc.codeAddr),
66 desc.codeSize) != EOK) {
67 LOG_JIT(ERROR) << "memcpy failed in CopyToCache";
68 return false;
69 }
70 #endif
71 return true;
72 }
73
SetText(const MachineCodeDesc & desc)74 bool MachineCode::SetText(const MachineCodeDesc &desc)
75 {
76 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
77 uint8_t *pText = textStart;
78 if (desc.rodataSizeBeforeTextAlign != 0) {
79 if (memcpy_s(pText, desc.rodataSizeBeforeTextAlign, // LCOV_EXCL_BR_LINE
80 reinterpret_cast<uint8_t*>(desc.rodataAddrBeforeText),
81 desc.rodataSizeBeforeText) != EOK) {
82 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
83 return false;
84 }
85 pText += desc.rodataSizeBeforeTextAlign;
86 }
87 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
88 !desc.isAsyncCompileMode) {
89 if (MachineCodeCopyToCache(desc, pText) == false) {
90 return false;
91 }
92 }
93 pText += desc.codeSizeAlign;
94 if (desc.rodataSizeAfterTextAlign != 0) {
95 if (memcpy_s(pText, desc.rodataSizeAfterTextAlign, // LCOV_EXCL_BR_LINE
96 reinterpret_cast<uint8_t*>(desc.rodataAddrAfterText),
97 desc.rodataSizeAfterText) != EOK) {
98 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
99 return false;
100 }
101 }
102 return true;
103 }
104
SetNonText(const MachineCodeDesc & desc,EntityId methodId)105 bool MachineCode::SetNonText(const MachineCodeDesc &desc, EntityId methodId)
106 {
107 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
108 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
109 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, // LCOV_EXCL_BR_LINE
110 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
111 desc.stackMapOrOffsetTableSize) != EOK) {
112 LOG_JIT(ERROR) << "memcpy fail in copy fast jit stackmap";
113 return false;
114 }
115
116 FuncEntryDes *funcEntry = reinterpret_cast<FuncEntryDes*>(desc.funcEntryDesAddr);
117 if (!funcEntry) {
118 LOG_JIT(ERROR) << "funcEntry is null.";
119 return false;
120 }
121 uint32_t cnt = desc.funcEntryDesSize / sizeof(FuncEntryDes);
122 ASSERT(cnt <= 2); // 2: jsfunction + deoptimize stub
123 for (uint32_t i = 0; i < cnt; i++) {
124 if (methodId == EntityId(funcEntry->indexInKindOrMethodId_)) {
125 uint64_t codeAddr = funcEntry->codeAddr_ +
126 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(textStart));
127 SetFuncAddr(codeAddr);
128 break;
129 }
130 funcEntry++;
131 }
132
133 SetIsFastCall(funcEntry->isFastCall_);
134 SetFpDeltaPrevFrameSp(funcEntry->fpDeltaPrevFrameSp_);
135 SetFuncSize(funcEntry->funcSize_);
136 SetCalleeRegisterNum(funcEntry->calleeRegisterNum_);
137 SetCalleeReg2OffsetArray(funcEntry->CalleeReg2Offset_);
138 return true;
139 }
140
SetData(JSThread * thread,const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize)141 bool MachineCode::SetData(JSThread *thread, const MachineCodeDesc &desc, JSHandle<Method> &method, size_t dataSize)
142 {
143 DISALLOW_GARBAGE_COLLECTION;
144 if (desc.codeType == MachineCodeType::BASELINE_CODE) {
145 return SetBaselineCodeData(thread, desc, method, dataSize);
146 }
147
148 if (desc.isHugeObj) {
149 SetLocalHeapAddress(0);
150 } else {
151 SetLocalHeapAddress(reinterpret_cast<uint64_t>(thread->GetEcmaVM()->GetHeap()));
152 }
153
154 SetOSROffset(MachineCode::INVALID_OSR_OFFSET);
155 SetOsrDeoptFlag(false);
156 SetOsrExecuteCnt(0);
157
158 size_t instrSize = desc.rodataSizeBeforeTextAlign + desc.codeSizeAlign + desc.rodataSizeAfterTextAlign;
159
160 SetInstructionsSize(instrSize);
161 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
162 SetHeapConstantTableSize(desc.heapConstantTableSizeAlign);
163 SetHeapConstantTableAddr(reinterpret_cast<uint64_t>(GetHeapConstantTableAddress()));
164 SetPayLoadSizeInBytes(dataSize);
165 if (Jit::GetInstance()->IsEnableJitFort()) {
166 SetInstructionsAddr(desc.instructionsAddr);
167 ASSERT(desc.instructionsAddr != 0);
168 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + desc.stackMapSizeAlign + desc.heapConstantTableSizeAlign) ||
169 dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign +
170 desc.heapConstantTableSizeAlign));
171 } else {
172 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + instrSize +
173 desc.stackMapSizeAlign + desc.heapConstantTableSizeAlign));
174 }
175 if (!SetText(desc)) {
176 return false;
177 }
178 if (!SetNonText(desc, method->GetMethodId())) {
179 return false;
180 }
181
182 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
183 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
184 CString methodName = method->GetRecordNameStr(thread) + "." + CString(method->GetMethodName(thread));
185 LOG_JIT(DEBUG) << "Fast JIT MachineCode :" << methodName << ", " << " text addr:" <<
186 reinterpret_cast<void*>(GetText()) << ", size:" << instrSize <<
187 ", stackMap addr:" << reinterpret_cast<void*>(stackmapAddr) <<
188 ", size:" << desc.stackMapSizeAlign;
189
190 if (!SetPageProtect(textStart, dataSize)) {
191 LOG_JIT(ERROR) << "MachineCode::SetData SetPageProtect failed";
192 return false;
193 }
194 return true;
195 }
196
SetBaselineCodeData(JSThread * thread,const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize)197 bool MachineCode::SetBaselineCodeData(JSThread *thread, const MachineCodeDesc &desc,
198 JSHandle<Method> &method, size_t dataSize)
199 {
200 DISALLOW_GARBAGE_COLLECTION;
201
202 size_t instrSizeAlign = desc.codeSizeAlign;
203 SetInstructionsSize(instrSizeAlign);
204
205 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
206 if (Jit::GetInstance()->IsEnableJitFort()) {
207 ASSERT(desc.instructionsAddr != 0);
208 ASSERT(dataSize == (desc.stackMapSizeAlign) || // reg. obj
209 dataSize == (instrSizeAlign + desc.stackMapSizeAlign)); // huge obj
210 SetInstructionsAddr(desc.instructionsAddr);
211 } else {
212 ASSERT(dataSize == (instrSizeAlign + desc.stackMapSizeAlign));
213 }
214 SetPayLoadSizeInBytes(dataSize);
215
216 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
217 if (Jit::GetInstance()->IsEnableJitFort()) {
218 // relax assert for now until machine code padding for JitFort resolved
219 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN) ||
220 IsAligned(reinterpret_cast<uintptr_t>(textStart), DATA_ALIGN));
221 } else {
222 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN));
223 }
224 uint8_t *pText = textStart;
225
226 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
227 !desc.isAsyncCompileMode) {
228 if (MachineCodeCopyToCache(desc, pText) == false) {
229 return false;
230 }
231 }
232 pText += instrSizeAlign;
233
234 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
235 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, // LCOV_EXCL_BR_LINE
236 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
237 desc.stackMapOrOffsetTableSize) != EOK) {
238 LOG_BASELINEJIT(ERROR) << "memcpy fail in copy fast baselineJIT offsetTable";
239 return false;
240 }
241
242 SetFuncAddr(reinterpret_cast<uintptr_t>(textStart));
243
244 CString methodName = method->GetRecordNameStr(thread) + "." + CString(method->GetMethodName(thread));
245 LOG_BASELINEJIT(DEBUG) << "BaselineCode :" << methodName << ", " << " text addr:" <<
246 reinterpret_cast<void*>(GetText()) << ", size:" << instrSizeAlign <<
247 ", stackMap addr: 0, size: 0";
248
249 if (!SetPageProtect(textStart, dataSize)) {
250 LOG_BASELINEJIT(ERROR) << "MachineCode::SetBaseLineCodeData SetPageProtect failed";
251 return false;
252 }
253 return true;
254 }
255
IsInText(const uintptr_t pc) const256 bool MachineCode::IsInText(const uintptr_t pc) const
257 {
258 uintptr_t textStart = GetText();
259 uintptr_t textEnd = textStart + GetTextSize();
260 return textStart <= pc && pc < textEnd;
261 }
262
CalCallSiteInfo() const263 std::tuple<uint64_t, uint8_t*, int, kungfu::CalleeRegAndOffsetVec> MachineCode::CalCallSiteInfo() const
264 {
265 uintptr_t textStart = GetText();
266 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
267 ASSERT(stackmapAddr != nullptr);
268
269 int delta = GetFpDeltaPrevFrameSp();
270 kungfu::CalleeRegAndOffsetVec calleeRegInfo;
271 for (uint32_t j = 0; j < GetCalleeRegisterNum(); j++) {
272 kungfu::LLVMStackMapType::DwarfRegType reg =
273 static_cast<kungfu::LLVMStackMapType::DwarfRegType>(GetCalleeReg2OffsetArray(2 * j));
274 kungfu::LLVMStackMapType::OffsetType offset =
275 static_cast<kungfu::LLVMStackMapType::OffsetType>(GetCalleeReg2OffsetArray(2 * j + 1));
276 kungfu::LLVMStackMapType::DwarfRegAndOffsetType regAndOffset = std::make_pair(reg, offset);
277 calleeRegInfo.emplace_back(regAndOffset);
278 }
279 auto ret = std::make_tuple(textStart, stackmapAddr, delta, calleeRegInfo);
280 return ret;
281 }
282
GetText() const283 uintptr_t MachineCode::GetText() const
284 {
285 if (Jit::GetInstance()->IsEnableJitFort()) {
286 return GetInstructionsAddr();
287 } else {
288 return GetNonTextAddress();
289 }
290 }
291
GetStackMapOrOffsetTableAddress() const292 uint8_t *MachineCode::GetStackMapOrOffsetTableAddress() const
293 {
294 if (Jit::GetInstance()->IsEnableJitFort()) {
295 // stackmap immediately follows MachineCode NonText area
296 return reinterpret_cast<uint8_t*>(GetNonTextAddress());
297 } else {
298 return reinterpret_cast<uint8_t*>(GetText() + GetInstructionsSize());
299 }
300 }
301
GetHeapConstantTableAddress() const302 uint8_t *MachineCode::GetHeapConstantTableAddress() const
303 {
304 return reinterpret_cast<uint8_t*>(GetStackMapOrOffsetTableAddress() + GetStackMapOrOffsetTableSize());
305 }
306
ProcessMarkObject()307 void MachineCode::ProcessMarkObject()
308 {
309 if (g_isEnableCMCGC) {
310 Heap* heap = reinterpret_cast<Heap*>(this->GetLocalHeapAddress());
311 // Skip HugeMachinecode or VM that is already destoryed
312 // We should implement a proper wait for VM destructor
313 if (heap && heap->GetMachineCodeSpace()) {
314 heap->GetMachineCodeSpace()->MarkJitFortMemAlive(this);
315 }
316 } else {
317 Region *region = Region::ObjectAddressToRange(this);
318 Heap *localHeap = reinterpret_cast<Heap *>(region->GetLocalHeap());
319 if (!localHeap) {
320 // it is a huge machine code object. skip
321 return;
322 }
323 ASSERT(localHeap->GetMachineCodeSpace());
324 if (localHeap->GetMachineCodeSpace()) {
325 localHeap->GetMachineCodeSpace()->MarkJitFortMemAlive(this);
326 }
327 }
328 }
329
330 } // namespace panda::ecmascript
331