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