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