1 /*
2 * Copyright (c) 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/compiler/aot_file/gdb_jit.h"
17 #include "llvm/BinaryFormat/ELF.h"
18 #include "ecmascript/log_wrapper.h"
19
20 #include <securec.h>
21 #include <mutex>
22
23 #ifndef PANDA_TARGET_MACOS
24 // Keep in sync with gdb/gdb/jit.h
25 extern "C" {
26 typedef enum {
27 JIT_NOACTION = 0,
28 JIT_REGISTER_FN,
29 JIT_UNREGISTER_FN
30 } jit_actions_t;
31
32 struct jit_code_entry {
33 struct jit_code_entry *next_entry;
34 struct jit_code_entry *prev_entry;
35 const char *symfile_addr;
36 uint64_t symfile_size;
37 const char *file_addr; // extend the standard
38 };
39
40 struct jit_descriptor {
41 uint32_t version;
42 // This should be jit_actions_t, but we want to be specific about the
43 // bit-width.
44 uint32_t action_flag;
45 struct jit_code_entry *relevant_entry;
46 struct jit_code_entry *first_entry;
47 };
48
49 // We put information about the JITed function in this global, which the
50 // debugger reads. Make sure to specify the version statically, because the
51 // debugger checks the version before we can set it during runtime.
52 struct jit_descriptor __jit_debug_descriptor = {
53 1, JIT_NOACTION, nullptr, nullptr
54 };
55
56 // Debuggers puts a breakpoint in this function.
__jit_debug_register_code()57 void __attribute__((noinline)) __jit_debug_register_code()
58 {
59 LOG_COMPILER(INFO) << "__jit_debug_register_code() is called.";
60 }
61 }
62
63 std::mutex g_descMutex;
64
65 namespace panda::ecmascript {
66 namespace jit_debug {
67 using namespace llvm::ELF;
68
69 static bool RegisterStubAnToDebuggerImpl(const char *fileAddr);
70
RegisterStubAnToDebugger(const char * fileAddr)71 void RegisterStubAnToDebugger(const char *fileAddr)
72 {
73 std::lock_guard<std::mutex> guard(g_descMutex);
74 auto entry = __jit_debug_descriptor.first_entry;
75 while (entry != nullptr && entry->file_addr != fileAddr) {
76 entry = entry->next_entry;
77 }
78 if (entry != nullptr) {
79 return;
80 }
81 if (RegisterStubAnToDebuggerImpl(fileAddr)) {
82 LOG_COMPILER(INFO) << "success to register stub.an to debugger.";
83 } else {
84 LOG_COMPILER(ERROR) << "Can't register stub.an to debugger.";
85 }
86 }
87
UnregisterStubAnFromDebugger(const char * fileAddr)88 void UnregisterStubAnFromDebugger(const char *fileAddr)
89 {
90 std::lock_guard<std::mutex> guard(g_descMutex);
91 auto entry = __jit_debug_descriptor.first_entry;
92 while (entry != nullptr && entry->file_addr != fileAddr) {
93 entry = entry->next_entry;
94 }
95 if (entry == nullptr) {
96 return;
97 }
98 if (entry->prev_entry != nullptr) {
99 entry->prev_entry->next_entry = entry->next_entry;
100 }
101 if (entry->next_entry != nullptr) {
102 entry->next_entry->prev_entry = entry->prev_entry;
103 }
104 __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
105 __jit_debug_descriptor.relevant_entry = entry;
106 __jit_debug_register_code();
107 __jit_debug_descriptor.relevant_entry = nullptr;
108 delete entry->symfile_addr;
109 delete entry;
110 }
111
112 template<typename T, typename U>
OffsetAlignUp(U * addr,uint64_t offset,uint32_t align)113 inline T OffsetAlignUp(U *addr, uint64_t offset, uint32_t align)
114 {
115 auto value = reinterpret_cast<uint64_t>(addr) + offset;
116 auto result = value;
117 if (align != 0 && (value % align != 0)) {
118 result = value + (align - (value % align));
119 }
120 return reinterpret_cast<T>(result);
121 }
122
123 struct StubAnInfo {
124 uintptr_t fileAddr;
125 Elf64_Ehdr *ehdr;
126 Elf64_Shdr *shdrTab;
127 uint32_t shStrIdx;
128 Elf64_Shdr *shStrHdr;
129 Elf64_Shdr *textHdr;
130 Elf64_Shdr *asmstubHdr;
131 Elf64_Shdr *symtabHdr;
132 Elf64_Shdr *strtabHdr;
133 uint64_t bcStubBegin;
134 uint64_t bcStubEnd;
135 uint32_t symCnt;
136 };
137
138 /*
139 * [0] file header
140 * [1] program header
141 * [2] shstrtab
142 * [3] strtab
143 * [4] symtab
144 * [5] .eh_frame
145 * [6] empty header
146 * [7] shstrtab-header
147 * [8] strtab-header
148 * [9] symtab-header
149 * [10] text-header
150 * [11] .eh_frame header
151 */
152 const char SHSTR[] = "\0.shstrtab\0.strtab\0.symtab\0.text\0.eh_frame";
153 const uint32_t SHSTRTAB_NAME = 1;
154 const uint32_t STRTAB_NAME = SHSTRTAB_NAME + strlen(".shstrtab") + 1;
155 const uint32_t SYMTAB_NAME = STRTAB_NAME + strlen(".strtab") + 1;
156 const uint32_t TEXT_NAME = SYMTAB_NAME + strlen(".symtab") + 1;
157 const uint32_t EH_FRAME_NAME = TEXT_NAME + strlen(".text") + 1;
158
159 const uint32_t SHSTRTAB_HDR_IDX = 1;
160 const uint32_t STRTAB_HDR_IDX = 2;
161 const uint32_t SYMTAB_HDR_IDX = 3;
162 const uint32_t TEXT_HDR_IDX = 4;
163
164 const uint32_t HEADER_CNT = 6;
165
InfoGetBind(unsigned char info)166 inline int InfoGetBind(unsigned char info)
167 {
168 const uint32_t shift = 4;
169 return info >> shift;
170 }
171
CollectStubAnInfo(uintptr_t fileAddr)172 StubAnInfo CollectStubAnInfo(uintptr_t fileAddr)
173 {
174 auto *ehdr = reinterpret_cast<Elf64_Ehdr *>(fileAddr);
175 auto *shdrTab = reinterpret_cast<Elf64_Shdr *>(fileAddr + ehdr->e_shoff);
176 uint32_t shStrIdx = ehdr->e_shstrndx;
177 Elf64_Shdr *shStrHdr = &shdrTab[shStrIdx];
178 const char *shstrtab = reinterpret_cast<const char *>(fileAddr + shStrHdr->sh_offset);
179 Elf64_Shdr *textHdr = nullptr;
180 Elf64_Shdr *asmstubHdr = nullptr;
181 Elf64_Shdr *symtabHdr = nullptr;
182 Elf64_Shdr *strtabHdr = nullptr;
183 for (uint32_t i = 0; i < ehdr->e_shnum; i++) {
184 Elf64_Shdr *shdr = &shdrTab[i];
185 const char *name = &shstrtab[shdr->sh_name];
186 if (strcmp(name, ".text") == 0) {
187 textHdr = shdr;
188 } else if (strcmp(name, ".ark_asmstub") == 0) {
189 asmstubHdr = shdr;
190 } else if (strcmp(name, ".symtab") == 0) {
191 symtabHdr = shdr;
192 } else if (strcmp(name, ".strtab") == 0) {
193 strtabHdr = shdr;
194 }
195 }
196 ASSERT(symtabHdr != nullptr);
197 Elf64_Sym *symtab = reinterpret_cast<Elf64_Sym *>(fileAddr + symtabHdr->sh_offset);
198 const char *strtab = reinterpret_cast<const char *>(fileAddr + strtabHdr->sh_offset);
199 uint32_t symCnt = 2;
200 uint64_t bcStubBegin = UINT64_MAX;
201 uint64_t bcStubEnd = 0;
202 for (uint32_t symIdx = 0; symIdx < symtabHdr->sh_size / symtabHdr->sh_entsize; symIdx++) {
203 Elf64_Sym *sym = symtab + symIdx;
204 if (InfoGetBind(sym->st_info) != STB_GLOBAL) {
205 continue;
206 }
207 if (strncmp(&strtab[sym->st_name], "BCStub", strlen("BCStub")) != 0) {
208 symCnt++;
209 } else {
210 if (sym->st_value < bcStubBegin) {
211 bcStubBegin = sym->st_value;
212 }
213 if (sym->st_value + sym->st_size > bcStubEnd) {
214 bcStubEnd = sym->st_value + sym->st_size;
215 }
216 }
217 }
218 return StubAnInfo {
219 fileAddr, ehdr, shdrTab, shStrIdx, shStrHdr, textHdr, asmstubHdr, symtabHdr, strtabHdr,
220 bcStubBegin, bcStubEnd, symCnt
221 };
222 }
223
CopyStrTab(uintptr_t baseAddr,const StubAnInfo & info)224 bool CopyStrTab(uintptr_t baseAddr, const StubAnInfo &info)
225 {
226 Elf64_Ehdr *newEhdr = reinterpret_cast<Elf64_Ehdr *>(baseAddr);
227 Elf64_Phdr *newPhdr = reinterpret_cast<Elf64_Phdr *>(newEhdr + 1);
228 char *shStrBuff = reinterpret_cast<char *>(newPhdr + 1);
229 if (memcpy_s(shStrBuff, sizeof(SHSTR), SHSTR, sizeof(SHSTR)) != EOK) {
230 return false;
231 }
232 char *newStrtab = shStrBuff + sizeof(SHSTR);
233 if (memcpy_s(newStrtab, info.strtabHdr->sh_size,
234 reinterpret_cast<void *>(info.fileAddr + info.strtabHdr->sh_offset), info.strtabHdr->sh_size) != EOK) {
235 return false;
236 }
237 const char bcStubName[] = "BCStubInterpreterRoutine";
238 if (memcpy_s((newStrtab + 1), sizeof(bcStubName), bcStubName, sizeof(bcStubName)) != EOK) {
239 return false;
240 }
241 return true;
242 }
243
ConstructSymTab(Elf64_Sym * newSymtab,const StubAnInfo & info)244 void ConstructSymTab(Elf64_Sym *newSymtab, const StubAnInfo &info)
245 {
246 Elf64_Sym *symtab = reinterpret_cast<Elf64_Sym *>(info.fileAddr + info.symtabHdr->sh_offset);
247 const char *strtab = reinterpret_cast<const char *>(info.fileAddr + info.strtabHdr->sh_offset);
248 memset_s(newSymtab, sizeof(Elf64_Sym), 0, sizeof(Elf64_Sym));
249 uint32_t newSymIdx = 1;
250 for (uint32_t symIdx = 0; symIdx < info.symtabHdr->sh_size / info.symtabHdr->sh_entsize; symIdx++) {
251 Elf64_Sym *src = symtab + symIdx;
252 if (InfoGetBind(src->st_info) == STB_GLOBAL
253 && strncmp(&strtab[src->st_name], "BCStub", strlen("BCStub")) != 0) {
254 auto dst = newSymtab + newSymIdx;
255 newSymIdx++;
256 *dst = *src;
257 dst->st_shndx = TEXT_HDR_IDX;
258 dst->st_value -= info.textHdr->sh_offset;
259 }
260 }
261 auto bcSym = newSymtab + newSymIdx;
262 bcSym->st_name = 1;
263 bcSym->st_info = newSymtab[1].st_info;
264 bcSym->st_other = newSymtab[1].st_other;
265 bcSym->st_shndx = TEXT_HDR_IDX;
266 bcSym->st_value = info.bcStubBegin - info.textHdr->sh_offset;
267 ASSERT(info.bcStubEnd >= info.bcStubBegin);
268 bcSym->st_size = info.bcStubEnd - info.bcStubBegin;
269 }
270
ConstructEhdrAndPhdr(Elf64_Ehdr * newEhdr,Elf64_Shdr * newShdrtab,uintptr_t baseAddr,const StubAnInfo & info)271 void ConstructEhdrAndPhdr(Elf64_Ehdr *newEhdr, Elf64_Shdr *newShdrtab, uintptr_t baseAddr, const StubAnInfo &info)
272 {
273 Elf64_Phdr *newPhdr = reinterpret_cast<Elf64_Phdr *>(newEhdr + 1);
274 {
275 *newEhdr = *info.ehdr;
276 newEhdr->e_flags = info.ehdr->e_flags;
277 newEhdr->e_machine = info.ehdr->e_machine;
278 if (memcpy_s(newEhdr->e_ident, sizeof(info.ehdr->e_ident),
279 info.ehdr->e_ident, sizeof(info.ehdr->e_ident)) != EOK) {
280 return;
281 }
282 newEhdr->e_version = 1;
283 newEhdr->e_phoff = sizeof(Elf64_Ehdr);
284 newEhdr->e_shoff = reinterpret_cast<uintptr_t>(newShdrtab) - baseAddr;
285 newEhdr->e_ehsize = sizeof(Elf64_Ehdr);
286 newEhdr->e_phentsize = sizeof(Elf64_Phdr);
287 newEhdr->e_phnum = 1;
288 newEhdr->e_shentsize = sizeof(Elf64_Shdr);
289 newEhdr->e_shnum = HEADER_CNT;
290 newEhdr->e_shstrndx = SHSTRTAB_HDR_IDX;
291 newEhdr->e_type = ET_REL;
292 newEhdr->e_entry = 0;
293 }
294 uintptr_t textAddr = info.textHdr->sh_offset + info.fileAddr;
295 uint64_t textSize = info.asmstubHdr->sh_offset + info.asmstubHdr->sh_size - info.textHdr->sh_offset;
296 {
297 newPhdr->p_type = PT_LOAD;
298 newPhdr->p_flags = PF_X | PF_R;
299 newPhdr->p_offset = textAddr - info.fileAddr;
300 newPhdr->p_vaddr = textAddr;
301 newPhdr->p_paddr = textAddr;
302 newPhdr->p_filesz = textSize;
303 newPhdr->p_memsz = textSize;
304 newPhdr->p_align = 0x1000;
305 }
306 }
307
ConstructShdrTab(Elf64_Shdr * newShdrTab,Elf64_Sym * newSymtab,uintptr_t baseAddr,void * ehFrame,uint32_t ehFrameSize,const StubAnInfo & info)308 void ConstructShdrTab(Elf64_Shdr *newShdrTab, Elf64_Sym *newSymtab, uintptr_t baseAddr, void *ehFrame,
309 uint32_t ehFrameSize, const StubAnInfo &info)
310 {
311 Elf64_Shdr hdr{};
312 Elf64_Shdr *emptyShdr = newShdrTab;
313 Elf64_Shdr *newShstrHdr = emptyShdr + 1;
314 Elf64_Shdr *newStrtabHdr = newShstrHdr + 1;
315 Elf64_Shdr *newSymHdr = newStrtabHdr + 1;
316 Elf64_Shdr *newTextHdr = newSymHdr + 1;
317 Elf64_Shdr *ehFrameHdr = newTextHdr + 1;
318 *emptyShdr = hdr;
319
320 newShstrHdr->sh_offset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr);
321 newShstrHdr->sh_size = sizeof(SHSTR);
322 newShstrHdr->sh_name = SHSTRTAB_NAME;
323 newShstrHdr->sh_addr = newStrtabHdr->sh_offset + baseAddr;
324 newShstrHdr->sh_type = SHT_STRTAB;
325 newShstrHdr->sh_flags = SHF_ALLOC;
326
327 newStrtabHdr->sh_offset = newShstrHdr->sh_offset + newShstrHdr->sh_size;
328 newStrtabHdr->sh_size = info.strtabHdr->sh_size;
329 newStrtabHdr->sh_name = STRTAB_NAME;
330 newStrtabHdr->sh_addr = newStrtabHdr->sh_offset + baseAddr;
331 newStrtabHdr->sh_addralign = 1;
332 newStrtabHdr->sh_type = SHT_STRTAB;
333 newStrtabHdr->sh_flags = SHF_ALLOC;
334 newStrtabHdr->sh_link = SHSTRTAB_HDR_IDX;
335
336 *newSymHdr = *info.symtabHdr;
337 newSymHdr->sh_offset = reinterpret_cast<uintptr_t>(newSymtab) - baseAddr;
338 newSymHdr->sh_size = info.symCnt * info.symtabHdr->sh_entsize;
339 newSymHdr->sh_entsize = info.symtabHdr->sh_entsize;
340 newSymHdr->sh_addralign = info.symtabHdr->sh_addralign;
341 newSymHdr->sh_name = SYMTAB_NAME;
342 newSymHdr->sh_addr = reinterpret_cast<uintptr_t>(newSymtab);
343 newSymHdr->sh_link = STRTAB_HDR_IDX;
344
345 newTextHdr->sh_offset = 0;
346 newTextHdr->sh_size = info.asmstubHdr->sh_offset + info.asmstubHdr->sh_size - info.textHdr->sh_offset;
347 newTextHdr->sh_name = TEXT_NAME;
348 newTextHdr->sh_addr = info.fileAddr + info.textHdr->sh_offset;
349 newTextHdr->sh_addralign = sizeof(uint32_t);
350 newTextHdr->sh_type = SHT_NOBITS;
351 newTextHdr->sh_flags = SHF_ALLOC | SHF_EXECINSTR;
352 newTextHdr->sh_link = SYMTAB_HDR_IDX;
353
354 ehFrameHdr->sh_offset = reinterpret_cast<uintptr_t>(ehFrame) - baseAddr;
355 ehFrameHdr->sh_size = ehFrameSize;
356 ehFrameHdr->sh_name = EH_FRAME_NAME;
357 ehFrameHdr->sh_addr = reinterpret_cast<uintptr_t>(ehFrame);
358 ehFrameHdr->sh_addralign = sizeof(uintptr_t);
359 ehFrameHdr->sh_type = SHT_PROGBITS;
360 ehFrameHdr->sh_flags = SHF_ALLOC;
361 ehFrameHdr->sh_link = TEXT_HDR_IDX;
362 }
363
CreateDebuggerElf(uintptr_t fileAddr,void ** result,uint64_t * elfSize)364 bool CreateDebuggerElf(uintptr_t fileAddr, void **result, uint64_t *elfSize)
365 {
366 auto info = CollectStubAnInfo(fileAddr);
367 std::vector<uint8_t> ehFrame {
368 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x78, 0x1e, 0x0, 0x0, 0x0,
369 0x0, 0x0, 0x0, 0x0, 0x4b, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0xe0, 0x73, 0xab,
370 0xff, 0xff, 0x0, 0x0, 0x30, 0x2c, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x1d, 0x0, 0x10,
371 0x1e, 0x17, 0x8d, 0x0, 0x12, 0x8, 0x18, 0x1c, 0x6, 0x8, 0x0, 0x29, 0x28, 0x7, 0x0, 0x8,
372 0x10, 0x1c, 0x6, 0x2f, 0xee, 0xff, 0x8, 0x8, 0x22, 0x10, 0x1d, 0x17, 0x8d, 0x0, 0x12, 0x8,
373 0x18, 0x1c, 0x6, 0x8, 0x0, 0x29, 0x28, 0x7, 0x0, 0x8, 0x10, 0x1c, 0x6, 0x2f, 0xee, 0xff,
374 0x8, 0x0, 0x22,
375 };
376 const uint32_t addrOff = 28;
377 const uint32_t lenOff = 36;
378 auto writeU64 = [&ehFrame](uint32_t idx, uint64_t data) {
379 for (uint32_t i = 0; i < sizeof(uint64_t); i++) {
380 ehFrame[idx + i] = (data >> (8 * i)) & 0xff;
381 }
382 };
383 writeU64(addrOff, info.bcStubBegin + fileAddr);
384 ASSERT(info.bcStubEnd >= info.bcStubBegin);
385 writeU64(lenOff, info.bcStubEnd - info.bcStubBegin);
386
387 uint32_t totalSize = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) + sizeof(SHSTR) + info.strtabHdr->sh_size;
388 totalSize += info.symtabHdr->sh_entsize * info.symCnt + sizeof(Elf64_Sym); // for align
389 totalSize += ehFrame.size() + sizeof(uintptr_t);
390 totalSize += sizeof(Elf64_Shdr) * HEADER_CNT + sizeof(Elf64_Shdr); // for align
391
392 char *buffer = new char[totalSize];
393 Elf64_Ehdr *newEhdr = reinterpret_cast<Elf64_Ehdr *>(buffer);
394 Elf64_Phdr *newPhdr = reinterpret_cast<Elf64_Phdr *>(newEhdr + 1);
395 const char *shStrBuff = reinterpret_cast<const char *>(newPhdr + 1);
396 const char *newStrtab = shStrBuff + sizeof(SHSTR);
397 if (!CopyStrTab(reinterpret_cast<uintptr_t>(buffer), info)) {
398 delete[] buffer;
399 return false;
400 }
401
402 auto newSymtab = OffsetAlignUp<Elf64_Sym *>(newStrtab, info.strtabHdr->sh_size, info.symtabHdr->sh_addralign);
403 ConstructSymTab(newSymtab, info);
404
405 auto ehFrameBuffer = OffsetAlignUp<char *>(newSymtab, info.symtabHdr->sh_entsize * info.symCnt, sizeof(uintptr_t));
406 if (memcpy_s(ehFrameBuffer, ehFrame.size(), ehFrame.data(), ehFrame.size()) != EOK) {
407 delete[] buffer;
408 return false;
409 }
410
411 auto newShdrtab = OffsetAlignUp<Elf64_Shdr *>(ehFrameBuffer, ehFrame.size(), sizeof(uintptr_t));
412 ConstructEhdrAndPhdr(newEhdr, newShdrtab, reinterpret_cast<uintptr_t>(buffer), info);
413 ConstructShdrTab(newShdrtab, newSymtab, reinterpret_cast<uintptr_t>(buffer), ehFrameBuffer, ehFrame.size(), info);
414
415 *result = reinterpret_cast<void *>(buffer);
416 *elfSize = totalSize;
417 return true;
418 }
419
RegisterStubAnToDebuggerImpl(const char * fileAddr)420 static bool RegisterStubAnToDebuggerImpl(const char *fileAddr)
421 {
422 auto *entry = new jit_code_entry;
423 if (!CreateDebuggerElf(reinterpret_cast<uintptr_t>(fileAddr),
424 reinterpret_cast<void **>(const_cast<char **>(&entry->symfile_addr)), &entry->symfile_size)) {
425 delete entry;
426 return false;
427 }
428 entry->prev_entry = nullptr;
429 entry->file_addr = fileAddr;
430
431 // Insert this entry at the head of the list.
432 jit_code_entry *nextEntry = __jit_debug_descriptor.first_entry;
433 entry->next_entry = nextEntry;
434 if (nextEntry) {
435 nextEntry->prev_entry = entry;
436 }
437
438 __jit_debug_descriptor.first_entry = entry;
439 __jit_debug_descriptor.relevant_entry = entry;
440 __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
441 __jit_debug_register_code();
442 return true;
443 }
444
445 }
446 }
447 #else
448 namespace panda::ecmascript {
449 namespace jit_debug {
RegisterStubAnToDebugger(const char * fileAddr)450 void RegisterStubAnToDebugger(const char *fileAddr)
451 {
452 LOG_COMPILER(INFO) << "MACOS doesn't support RegisterStubAnToDebugger, fileAddr is" << fileAddr;
453 }
454
UnregisterStubAnFromDebugger(const char * fileAddr)455 void UnregisterStubAnFromDebugger(const char *fileAddr)
456 {
457 LOG_COMPILER(INFO) << "MACOS doesn't support RegisterStubAnToDebugger, fileAddr is" << fileAddr;
458 }
459 }
460 }
461 #endif
462