summaryrefslogtreecommitdiff
path: root/src/vm/gdbjit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/gdbjit.cpp')
-rw-r--r--src/vm/gdbjit.cpp1185
1 files changed, 1185 insertions, 0 deletions
diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp
new file mode 100644
index 0000000000..9f9c116820
--- /dev/null
+++ b/src/vm/gdbjit.cpp
@@ -0,0 +1,1185 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// File: gdbjit.cpp
+//
+
+//
+// NotifyGdb implementation.
+//
+//*****************************************************************************
+
+#include "common.h"
+#include "gdbjit.h"
+#include "gdbjithelpers.h"
+
+struct DebuggerILToNativeMap
+{
+ ULONG ilOffset;
+ ULONG nativeStartOffset;
+ ULONG nativeEndOffset;
+ ICorDebugInfo::SourceTypes source;
+};
+BYTE* DebugInfoStoreNew(void * pData, size_t cBytes)
+{
+ return new (nothrow) BYTE[cBytes];
+}
+
+/* Get IL to native offsets map */
+HRESULT
+GetMethodNativeMap(MethodDesc* methodDesc,
+ ULONG32* numMap,
+ DebuggerILToNativeMap** map)
+{
+ // Use the DebugInfoStore to get IL->Native maps.
+ // It doesn't matter whether we're jitted, ngenned etc.
+
+ DebugInfoRequest request;
+ TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode());
+ request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr);
+
+ // Bounds info.
+ ULONG32 countMapCopy;
+ NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL);
+
+ BOOL success = DebugInfoManager::GetBoundariesAndVars(request,
+ DebugInfoStoreNew,
+ NULL, // allocator
+ &countMapCopy,
+ &mapCopy,
+ NULL,
+ NULL);
+
+ if (!success)
+ {
+ return E_FAIL;
+ }
+
+ // Need to convert map formats.
+ *numMap = countMapCopy;
+
+ *map = new (nothrow) DebuggerILToNativeMap[countMapCopy];
+ if (!*map)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ ULONG32 i;
+ for (i = 0; i < *numMap; i++)
+ {
+ (*map)[i].ilOffset = mapCopy[i].ilOffset;
+ (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset;
+ if (i > 0)
+ {
+ (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset;
+ }
+ (*map)[i].source = mapCopy[i].source;
+ }
+ if (*numMap >= 1)
+ {
+ (*map)[i - 1].nativeEndOffset = 0;
+ }
+ return S_OK;
+}
+
+/* Get mapping of IL offsets to source line numbers */
+HRESULT
+GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned int &symInfoLen)
+{
+ DebuggerILToNativeMap* map = NULL;
+
+ ULONG32 numMap;
+
+ if (!getInfoForMethodDelegate)
+ return E_FAIL;
+
+ if (GetMethodNativeMap(MethodDescPtr, &numMap, &map) != S_OK)
+ return E_FAIL;
+
+ const Module* mod = MethodDescPtr->GetMethodTable()->GetModule();
+ SString modName = mod->GetFile()->GetPath();
+ if (modName.IsEmpty())
+ return E_FAIL;
+
+ StackScratchBuffer scratch;
+ const char* szModName = modName.GetUTF8(scratch);
+
+ MethodDebugInfo* methodDebugInfo = new (nothrow) MethodDebugInfo();
+ if (methodDebugInfo == nullptr)
+ return E_OUTOFMEMORY;
+
+ methodDebugInfo->points = (SequencePointInfo*) CoTaskMemAlloc(sizeof(SequencePointInfo) * numMap);
+ if (methodDebugInfo->points == nullptr)
+ return E_OUTOFMEMORY;
+
+ methodDebugInfo->size = numMap;
+
+ if (getInfoForMethodDelegate(szModName, MethodDescPtr->GetMemberDef(), *methodDebugInfo) == FALSE)
+ return E_FAIL;
+
+ symInfoLen = methodDebugInfo->size;
+ *symInfo = new (nothrow) SymbolsInfo[symInfoLen];
+ if (*symInfo == nullptr)
+ return E_FAIL;
+
+ for (ULONG32 i = 0; i < symInfoLen; i++)
+ {
+ for (ULONG32 j = 0; j < numMap; j++)
+ {
+ if (methodDebugInfo->points[i].ilOffset == map[j].ilOffset)
+ {
+ SymbolsInfo& s = (*symInfo)[i];
+ const SequencePointInfo& sp = methodDebugInfo->points[i];
+
+ s.nativeOffset = map[j].nativeStartOffset;
+ s.ilOffset = map[j].ilOffset;
+ s.fileIndex = 0;
+ //wcscpy(s.fileName, sp.fileName);
+ int len = WideCharToMultiByte(CP_UTF8, 0, sp.fileName, -1, s.fileName, sizeof(s.fileName), NULL, NULL);
+ s.fileName[len] = 0;
+ s.lineNumber = sp.lineNumber;
+ }
+ }
+ }
+
+ CoTaskMemFree(methodDebugInfo->points);
+ return S_OK;
+}
+
+// GDB JIT interface
+typedef enum
+{
+ JIT_NOACTION = 0,
+ JIT_REGISTER_FN,
+ JIT_UNREGISTER_FN
+} jit_actions_t;
+
+struct jit_code_entry
+{
+ struct jit_code_entry *next_entry;
+ struct jit_code_entry *prev_entry;
+ const char *symfile_addr;
+ UINT64 symfile_size;
+};
+
+struct jit_descriptor
+{
+ UINT32 version;
+ /* This type should be jit_actions_t, but we use uint32_t
+ to be explicit about the bitwidth. */
+ UINT32 action_flag;
+ struct jit_code_entry *relevant_entry;
+ struct jit_code_entry *first_entry;
+};
+// GDB puts a breakpoint in this function.
+// To prevent from inlining we add noinline attribute and inline assembler statement.
+extern "C"
+void __attribute__((noinline)) __jit_debug_register_code() { __asm__(""); };
+
+/* Make sure to specify the version statically, because the
+ debugger may check the version before we can set it. */
+struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };
+
+// END of GDB JIT interface
+
+/* Predefined section names */
+const char* SectionNames[] = {
+ "", ".text", ".shstrtab", ".debug_str", ".debug_abbrev", ".debug_info",
+ ".debug_pubnames", ".debug_pubtypes", ".debug_line", ".symtab", ".strtab", ""
+};
+
+const int SectionNamesCount = sizeof(SectionNames) / sizeof(SectionNames[0]);
+
+/* Static data for section headers */
+struct SectionHeader {
+ uint32_t m_type;
+ uint64_t m_flags;
+} Sections[] = {
+ {SHT_NULL, 0},
+ {SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR},
+ {SHT_STRTAB, 0},
+ {SHT_PROGBITS, SHF_MERGE | SHF_STRINGS },
+ {SHT_PROGBITS, 0},
+ {SHT_PROGBITS, 0},
+ {SHT_PROGBITS, 0},
+ {SHT_PROGBITS, 0},
+ {SHT_PROGBITS, 0},
+ {SHT_SYMTAB, 0},
+ {SHT_STRTAB, 0},
+};
+
+/* Static data for .debug_str section */
+const char* DebugStrings[] = {
+ "CoreCLR", "" /* module name */, "" /* module path */, "" /* method name */, "int"
+};
+
+const int DebugStringCount = sizeof(DebugStrings) / sizeof(DebugStrings[0]);
+
+/* Static data for .debug_abbrev */
+const unsigned char AbbrevTable[] = {
+ 1, DW_TAG_compile_unit, DW_CHILDREN_yes,
+ DW_AT_producer, DW_FORM_strp, DW_AT_language, DW_FORM_data2, DW_AT_name, DW_FORM_strp,
+ DW_AT_stmt_list, DW_FORM_sec_offset, 0, 0,
+ 2, DW_TAG_subprogram, DW_CHILDREN_no,
+ DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_data1, DW_AT_decl_line, DW_FORM_data1,
+ DW_AT_type, DW_FORM_ref4, DW_AT_external, DW_FORM_flag_present, 0, 0,
+ 3, DW_TAG_base_type, DW_CHILDREN_no,
+ DW_AT_name, DW_FORM_strp, DW_AT_encoding, DW_FORM_data1, DW_AT_byte_size, DW_FORM_data1,0, 0,
+ 0
+};
+
+const int AbbrevTableSize = sizeof(AbbrevTable);
+
+/* Static data for .debug_line, including header */
+#define DWARF_LINE_BASE (-5)
+#define DWARF_LINE_RANGE 14
+#define DWARF_OPCODE_BASE 13
+
+DwarfLineNumHeader LineNumHeader = {
+ 0, 2, 0, 1, 1, DWARF_LINE_BASE, DWARF_LINE_RANGE, DWARF_OPCODE_BASE, {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}
+};
+
+/* Static data for .debug_info */
+struct __attribute__((packed)) DebugInfo
+{
+ uint8_t m_cu_abbrev;
+ uint32_t m_prod_off;
+ uint16_t m_lang;
+ uint32_t m_cu_name;
+ uint32_t m_line_num;
+
+ uint8_t m_sub_abbrev;
+ uint32_t m_sub_name;
+ uint8_t m_file, m_line;
+ uint32_t m_sub_type;
+
+ uint8_t m_type_abbrev;
+ uint32_t m_type_name;
+ uint8_t m_encoding;
+ uint8_t m_byte_size;
+} debugInfo = {
+ 1, 0, DW_LANG_C89, 0, 0,
+ 2, 0, 1, 1, 37,
+ 3, 0, DW_ATE_signed, 4
+};
+
+/* static data for symbol strings */
+const char* SymbolNames[] = {
+ "", ""
+};
+
+
+/* Create ELF/DWARF debug info for jitted method */
+void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr)
+{
+ PCODE pCode = MethodDescPtr->GetNativeCode();
+
+ if (pCode == NULL)
+ return;
+ unsigned int symInfoLen = 0;
+ NewArrayHolder<SymbolsInfo> symInfo = nullptr;
+
+ /* Get method name & size of jitted code */
+ LPCUTF8 methodName = MethodDescPtr->GetName();
+ EECodeInfo codeInfo(pCode);
+ TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken());
+
+#ifdef _TARGET_ARM_
+ pCode &= ~1; // clear thumb flag for debug info
+#endif
+
+ /* Get module name */
+ const Module* mod = MethodDescPtr->GetMethodTable()->GetModule();
+ SString modName = mod->GetFile()->GetPath();
+ StackScratchBuffer scratch;
+ const char* szModName = modName.GetUTF8(scratch);
+ const char *szModulePath, *szModuleFile;
+ SplitPathname(szModName, szModulePath, szModuleFile);
+
+
+ int length = MultiByteToWideChar(CP_UTF8, 0, szModuleFile, -1, NULL, 0);
+ if (length == 0)
+ return;
+ NewArrayHolder<WCHAR> wszModuleFile = new (nothrow) WCHAR[length+1];
+ length = MultiByteToWideChar(CP_UTF8, 0, szModuleFile, -1, wszModuleFile, length);
+
+ if (length == 0)
+ return;
+
+ static NewArrayHolder<WCHAR> wszModuleNames = nullptr;
+ DWORD cCharsNeeded = 0;
+
+ // Get names of interesting modules from environment
+ if (wszModuleNames == nullptr)
+ {
+ cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), NULL, 0);
+
+ if((cCharsNeeded == 0) || (cCharsNeeded >= MAX_LONGPATH))
+ return;
+ wszModuleNames = new WCHAR[cCharsNeeded+1];
+ cCharsNeeded = GetEnvironmentVariableW(W("CORECLR_GDBJIT"), wszModuleNames, cCharsNeeded);
+ if(cCharsNeeded == 0)
+ return;
+ }
+ else
+ {
+ cCharsNeeded = wcslen(wszModuleNames);
+ }
+
+ BOOL isUserDebug = FALSE;
+
+ NewArrayHolder<WCHAR> wszModuleName = new WCHAR[cCharsNeeded+1];
+ LPWSTR pComma = wcsstr(wszModuleNames, W(","));
+ LPWSTR tmp = wszModuleNames;
+
+ while (pComma != NULL)
+ {
+ wcsncpy(wszModuleName, tmp, pComma - tmp);
+ wszModuleName[pComma - tmp] = W('\0');
+
+ if (wcscmp(wszModuleName, wszModuleFile) == 0)
+ {
+ isUserDebug = TRUE;
+ break;
+ }
+ tmp = pComma + 1;
+ pComma = wcsstr(tmp, W(","));
+ }
+ if (isUserDebug == FALSE)
+ {
+ wcsncpy(wszModuleName, tmp, wcslen(tmp));
+ wszModuleName[wcslen(tmp)] = W('\0');
+ if (wcscmp(wszModuleName, wszModuleFile) == 0)
+ {
+ isUserDebug = TRUE;
+ }
+ }
+
+ if (isUserDebug == FALSE)
+ return;
+
+ /* Get debug info for method from portable PDB */
+ HRESULT hr = GetDebugInfoFromPDB(MethodDescPtr, &symInfo, symInfoLen);
+ if (FAILED(hr) || symInfoLen == 0)
+ {
+ return;
+ }
+
+ MemBuf elfHeader, sectHeaders, sectStr, sectSymTab, sectStrTab, dbgInfo, dbgAbbrev, dbgPubname, dbgPubType, dbgLine,
+ dbgStr, elfFile;
+
+ /* Build .debug_abbrev section */
+ if (!BuildDebugAbbrev(dbgAbbrev))
+ {
+ return;
+ }
+
+ /* Build .debug_line section */
+ if (!BuildLineTable(dbgLine, pCode, symInfo, symInfoLen))
+ {
+ return;
+ }
+
+ DebugStrings[1] = szModuleFile;
+ DebugStrings[3] = methodName;
+
+ /* Build .debug_str section */
+ if (!BuildDebugStrings(dbgStr))
+ {
+ return;
+ }
+
+ /* Build .debug_info section */
+ if (!BuildDebugInfo(dbgInfo))
+ {
+ return;
+ }
+
+ /* Build .debug_pubname section */
+ if (!BuildDebugPub(dbgPubname, methodName, dbgInfo.MemSize, 26))
+ {
+ return;
+ }
+
+ /* Build debug_pubtype section */
+ if (!BuildDebugPub(dbgPubType, "int", dbgInfo.MemSize, 37))
+ {
+ return;
+ }
+
+ /* Build .strtab section */
+ SymbolNames[1] = methodName;
+ if (!BuildStringTableSection(sectStrTab))
+ {
+ return;
+ }
+ /* Build .symtab section */
+ if (!BuildSymbolTableSection(sectSymTab, pCode, codeSize))
+ {
+ return;
+ }
+
+
+ /* Build section names section */
+ if (!BuildSectionNameTable(sectStr))
+ {
+ return;
+ }
+
+ /* Build section headers table */
+ if (!BuildSectionTable(sectHeaders))
+ {
+ return;
+ }
+
+ /* Patch section offsets & sizes */
+ long offset = sizeof(Elf_Ehdr);
+ Elf_Shdr* pShdr = reinterpret_cast<Elf_Shdr*>(sectHeaders.MemPtr.GetValue());
+ ++pShdr; // .text
+ pShdr->sh_addr = pCode;
+ pShdr->sh_size = codeSize;
+ ++pShdr; // .shstrtab
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = sectStr.MemSize;
+ offset += sectStr.MemSize;
+ ++pShdr; // .debug_str
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgStr.MemSize;
+ offset += dbgStr.MemSize;
+ ++pShdr; // .debug_abbrev
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgAbbrev.MemSize;
+ offset += dbgAbbrev.MemSize;
+ ++pShdr; // .debug_info
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgInfo.MemSize;
+ offset += dbgInfo.MemSize;
+ ++pShdr; // .debug_pubnames
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgPubname.MemSize;
+ offset += dbgPubname.MemSize;
+ ++pShdr; // .debug_pubtypes
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgPubType.MemSize;
+ offset += dbgPubType.MemSize;
+ ++pShdr; // .debug_line
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = dbgLine.MemSize;
+ offset += dbgLine.MemSize;
+ ++pShdr; // .symtab
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = sectSymTab.MemSize;
+ pShdr->sh_link = 10;
+ offset += sectSymTab.MemSize;
+ ++pShdr; // .strtab
+ pShdr->sh_offset = offset;
+ pShdr->sh_size = sectStrTab.MemSize;
+ offset += sectStrTab.MemSize;
+
+ /* Build ELF header */
+ if (!BuildELFHeader(elfHeader))
+ {
+ return;
+ }
+ Elf_Ehdr* header = reinterpret_cast<Elf_Ehdr*>(elfHeader.MemPtr.GetValue());
+#ifdef _TARGET_ARM_
+ header->e_flags = EF_ARM_EABI_VER5;
+#ifdef ARM_SOFTFP
+ header->e_flags |= EF_ARM_SOFT_FLOAT;
+#else
+ header->e_flags |= EF_ARM_VFP_FLOAT;
+#endif
+#endif
+ header->e_shoff = offset;
+ header->e_shentsize = sizeof(Elf_Shdr);
+ header->e_shnum = SectionNamesCount - 1;
+ header->e_shstrndx = 2;
+
+ /* Build ELF image in memory */
+ elfFile.MemSize = elfHeader.MemSize + sectStr.MemSize + dbgStr.MemSize + dbgAbbrev.MemSize + dbgInfo.MemSize +
+ dbgPubname.MemSize + dbgPubType.MemSize + dbgLine.MemSize + sectSymTab.MemSize +
+ sectStrTab.MemSize + sectHeaders.MemSize;
+ elfFile.MemPtr = new (nothrow) char[elfFile.MemSize];
+ if (elfFile.MemPtr == nullptr)
+ {
+ return;
+ }
+
+ /* Copy section data */
+ offset = 0;
+ memcpy(elfFile.MemPtr, elfHeader.MemPtr, elfHeader.MemSize);
+ offset += elfHeader.MemSize;
+ memcpy(elfFile.MemPtr + offset, sectStr.MemPtr, sectStr.MemSize);
+ offset += sectStr.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgStr.MemPtr, dbgStr.MemSize);
+ offset += dbgStr.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgAbbrev.MemPtr, dbgAbbrev.MemSize);
+ offset += dbgAbbrev.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgInfo.MemPtr, dbgInfo.MemSize);
+ offset += dbgInfo.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgPubname.MemPtr, dbgPubname.MemSize);
+ offset += dbgPubname.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgPubType.MemPtr, dbgPubType.MemSize);
+ offset += dbgPubType.MemSize;
+ memcpy(elfFile.MemPtr + offset, dbgLine.MemPtr, dbgLine.MemSize);
+ offset += dbgLine.MemSize;
+ memcpy(elfFile.MemPtr + offset, sectSymTab.MemPtr, sectSymTab.MemSize);
+ offset += sectSymTab.MemSize;
+ memcpy(elfFile.MemPtr + offset, sectStrTab.MemPtr, sectStrTab.MemSize);
+ offset += sectStrTab.MemSize;
+
+ memcpy(elfFile.MemPtr + offset, sectHeaders.MemPtr, sectHeaders.MemSize);
+
+#ifdef GDBJIT_DUMPELF
+ DumpElf(methodName, elfFile);
+#endif
+
+ /* Create GDB JIT structures */
+ jit_code_entry* jit_symbols = new (nothrow) jit_code_entry;
+
+ if (jit_symbols == nullptr)
+ {
+ return;
+ }
+
+ /* Fill the new entry */
+ jit_symbols->next_entry = jit_symbols->prev_entry = 0;
+ jit_symbols->symfile_addr = elfFile.MemPtr;
+ jit_symbols->symfile_size = elfFile.MemSize;
+
+ /* Link into list */
+ jit_code_entry *head = __jit_debug_descriptor.first_entry;
+ __jit_debug_descriptor.first_entry = jit_symbols;
+ if (head != 0)
+ {
+ jit_symbols->next_entry = head;
+ head->prev_entry = jit_symbols;
+ }
+
+ /* Notify the debugger */
+ __jit_debug_descriptor.relevant_entry = jit_symbols;
+ __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+ __jit_debug_register_code();
+
+}
+
+void NotifyGdb::MethodDropped(MethodDesc* MethodDescPtr)
+{
+ PCODE pCode = MethodDescPtr->GetNativeCode();
+
+ if (pCode == NULL)
+ return;
+
+ /* Find relevant entry */
+ for (jit_code_entry* jit_symbols = __jit_debug_descriptor.first_entry; jit_symbols != 0; jit_symbols = jit_symbols->next_entry)
+ {
+ const char* ptr = jit_symbols->symfile_addr;
+ uint64_t size = jit_symbols->symfile_size;
+
+ const Elf_Ehdr* pEhdr = reinterpret_cast<const Elf_Ehdr*>(ptr);
+ const Elf_Shdr* pShdr = reinterpret_cast<const Elf_Shdr*>(ptr + pEhdr->e_shoff);
+ ++pShdr; // bump to .text section
+ if (pShdr->sh_addr == pCode)
+ {
+ /* Notify the debugger */
+ __jit_debug_descriptor.relevant_entry = jit_symbols;
+ __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN;
+ __jit_debug_register_code();
+
+ /* Free memory */
+ delete[] ptr;
+
+ /* Unlink from list */
+ if (jit_symbols->prev_entry == 0)
+ __jit_debug_descriptor.first_entry = jit_symbols->next_entry;
+ else
+ jit_symbols->prev_entry->next_entry = jit_symbols->next_entry;
+ delete jit_symbols;
+ break;
+ }
+ }
+}
+
+/* Build the DWARF .debug_line section */
+bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines)
+{
+ MemBuf fileTable, lineProg;
+
+ /* Build file table */
+ if (!BuildFileTable(fileTable, lines, nlines))
+ return false;
+ /* Build line info program */
+ if (!BuildLineProg(lineProg, startAddr, lines, nlines))
+ {
+ return false;
+ }
+
+ buf.MemSize = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize + lineProg.MemSize;
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+
+ if (buf.MemPtr == nullptr)
+ {
+ return false;
+ }
+
+ /* Fill the line info header */
+ DwarfLineNumHeader* header = reinterpret_cast<DwarfLineNumHeader*>(buf.MemPtr.GetValue());
+ memcpy(buf.MemPtr, &LineNumHeader, sizeof(DwarfLineNumHeader));
+ header->m_length = buf.MemSize - sizeof(uint32_t);
+ header->m_hdr_length = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize - 2 * sizeof(uint32_t) - sizeof(uint16_t);
+ buf.MemPtr[sizeof(DwarfLineNumHeader)] = 0; // this is for missing directory table
+ /* copy file table */
+ memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1, fileTable.MemPtr, fileTable.MemSize);
+ /* copy line program */
+ memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize, lineProg.MemPtr, lineProg.MemSize);
+
+ return true;
+}
+
+/* Buid the source files table for DWARF source line info */
+bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines)
+{
+ const char** files = nullptr;
+ unsigned nfiles = 0;
+
+ /* GetValue file names and replace them with indices in file table */
+ files = new (nothrow) const char*[nlines];
+ if (files == nullptr)
+ return false;
+ for (unsigned i = 0; i < nlines; ++i)
+ {
+ const char *filePath, *fileName;
+ SplitPathname(lines[i].fileName, filePath, fileName);
+
+ /* if this isn't first then we already added file, so adjust index */
+ lines[i].fileIndex = (nfiles) ? (nfiles - 1) : (nfiles);
+
+ bool found = false;
+ for (int j = 0; j < nfiles; ++j)
+ {
+ if (strcmp(fileName, files[j]) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ /* add new source file */
+ if (!found)
+ {
+ files[nfiles++] = fileName;
+ }
+ }
+
+ /* build file table */
+ unsigned totalSize = 0;
+
+ for (unsigned i = 0; i < nfiles; ++i)
+ {
+ totalSize += strlen(files[i]) + 1 + 3;
+ }
+ totalSize += 1;
+
+ buf.MemSize = totalSize;
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+
+ if (buf.MemPtr == nullptr)
+ {
+ delete[] files;
+ return false;
+ }
+
+ /* copy collected file names */
+ char *ptr = buf.MemPtr;
+ for (unsigned i = 0; i < nfiles; ++i)
+ {
+ strcpy(ptr, files[i]);
+ ptr += strlen(files[i]) + 1;
+ // three LEB128 entries which we don't care
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ }
+ // final zero byte
+ *ptr = 0;
+
+ delete[] files;
+ return true;
+}
+
+/* Command to set absolute address */
+void NotifyGdb::IssueSetAddress(char*& ptr, PCODE addr)
+{
+ *ptr++ = 0;
+ *ptr++ = ADDRESS_SIZE + 1;
+ *ptr++ = DW_LNE_set_address;
+ *reinterpret_cast<PCODE*>(ptr) = addr;
+ ptr += ADDRESS_SIZE;
+}
+
+/* End of line program */
+void NotifyGdb::IssueEndOfSequence(char*& ptr)
+{
+ *ptr++ = 0;
+ *ptr++ = 1;
+ *ptr++ = DW_LNE_end_sequence;
+}
+
+/* Command w/o parameters */
+void NotifyGdb::IssueSimpleCommand(char*& ptr, uint8_t command)
+{
+ *ptr++ = command;
+}
+
+/* Command with one LEB128 parameter */
+void NotifyGdb::IssueParamCommand(char*& ptr, uint8_t command, char* param, int param_size)
+{
+ *ptr++ = command;
+ while (param_size-- > 0)
+ {
+ *ptr++ = *param++;
+ }
+}
+
+/* Special command moves address, line number and issue one row to source line matrix */
+void NotifyGdb::IssueSpecialCommand(char*& ptr, int8_t line_shift, uint8_t addr_shift)
+{
+ *ptr++ = (line_shift - DWARF_LINE_BASE) + addr_shift * DWARF_LINE_RANGE + DWARF_OPCODE_BASE;
+}
+
+/* Check to see if given shifts are fit into one byte command */
+bool NotifyGdb::FitIntoSpecialOpcode(int8_t line_shift, uint8_t addr_shift)
+{
+ unsigned opcode = (line_shift - DWARF_LINE_BASE) + addr_shift * DWARF_LINE_RANGE + DWARF_OPCODE_BASE;
+
+ return opcode < 255;
+}
+
+/* Build program for DWARF source line section */
+bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines)
+{
+ static char cnv_buf[16];
+
+ /* reserve memory assuming worst case: one extended and one special plus advance line command for each line*/
+ buf.MemSize = 3 + ADDRESS_SIZE /* initial set address command */
+ + 1 /* set prolog end command */
+ + 6 /* set file command */
+ + nlines * 6 /* advance line commands */
+ + nlines * (4 + ADDRESS_SIZE) /* 1 extended + 1 special command */
+ + 3; /* end of sequence command */
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+ char* ptr = buf.MemPtr;
+
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ /* set absolute start address */
+ IssueSetAddress(ptr, startAddr);
+ IssueSimpleCommand(ptr, DW_LNS_set_prologue_end);
+
+ int prevLine = 1, prevAddr = 0, prevFile = 0;
+
+ for (int i = 0; i < nlines; ++i)
+ {
+ /* different source file */
+ if (lines[i].fileIndex != prevFile)
+ {
+ int len = Leb128Encode(static_cast<uint32_t>(lines[i].fileIndex+1), cnv_buf, sizeof(cnv_buf));
+ IssueParamCommand(ptr, DW_LNS_set_file, cnv_buf, len);
+ prevFile = lines[i].fileIndex;
+ }
+ /* too big line number shift */
+ if (lines[i].lineNumber - prevLine > (DWARF_LINE_BASE + DWARF_LINE_RANGE - 1))
+ {
+ int len = Leb128Encode(static_cast<int32_t>(lines[i].lineNumber - prevLine), cnv_buf, sizeof(cnv_buf));
+ IssueParamCommand(ptr, DW_LNS_advance_line, cnv_buf, len);
+ prevLine = lines[i].lineNumber;
+ }
+ /* first try special opcode */
+ if (FitIntoSpecialOpcode(lines[i].lineNumber - prevLine, lines[i].nativeOffset - prevAddr))
+ IssueSpecialCommand(ptr, lines[i].lineNumber - prevLine, lines[i].nativeOffset - prevAddr);
+ else
+ {
+ IssueSetAddress(ptr, startAddr + lines[i].nativeOffset);
+ IssueSpecialCommand(ptr, lines[i].lineNumber - prevLine, 0);
+ }
+
+ prevLine = lines[i].lineNumber;
+ prevAddr = lines[i].nativeOffset;
+ }
+
+ IssueEndOfSequence(ptr);
+
+ buf.MemSize = ptr - buf.MemPtr;
+ return true;
+}
+
+/* Build the DWARF .debug_str section */
+bool NotifyGdb::BuildDebugStrings(MemBuf& buf)
+{
+ uint32_t totalLength = 0;
+
+ /* calculate total section size */
+ for (int i = 0; i < DebugStringCount; ++i)
+ {
+ totalLength += strlen(DebugStrings[i]) + 1;
+ }
+
+ buf.MemSize = totalLength;
+ buf.MemPtr = new (nothrow) char[totalLength];
+
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ /* copy strings */
+ char* bufPtr = buf.MemPtr;
+ for (int i = 0; i < DebugStringCount; ++i)
+ {
+ strcpy(bufPtr, DebugStrings[i]);
+ bufPtr += strlen(DebugStrings[i]) + 1;
+ }
+
+ return true;
+}
+
+/* Build the DWARF .debug_abbrev section */
+bool NotifyGdb::BuildDebugAbbrev(MemBuf& buf)
+{
+ buf.MemPtr = new (nothrow) char[AbbrevTableSize];
+ buf.MemSize = AbbrevTableSize;
+
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ memcpy(buf.MemPtr, AbbrevTable, AbbrevTableSize);
+ return true;
+}
+
+/* Build tge DWARF .debug_info section */
+bool NotifyGdb::BuildDebugInfo(MemBuf& buf)
+{
+ buf.MemSize = sizeof(DwarfCompUnit) + sizeof(DebugInfo) + 1;
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ /* Compile uint header */
+ DwarfCompUnit* cu = reinterpret_cast<DwarfCompUnit*>(buf.MemPtr.GetValue());
+ cu->m_length = buf.MemSize - sizeof(uint32_t);
+ cu->m_version = 4;
+ cu->m_abbrev_offset = 0;
+ cu->m_addr_size = ADDRESS_SIZE;
+
+ /* copy debug information */
+ DebugInfo* di = reinterpret_cast<DebugInfo*>(buf.MemPtr + sizeof(DwarfCompUnit));
+ memcpy(buf.MemPtr + sizeof(DwarfCompUnit), &debugInfo, sizeof(DebugInfo));
+ di->m_prod_off = 0;
+ di->m_cu_name = strlen(DebugStrings[0]) + 1;
+ di->m_sub_name = strlen(DebugStrings[0]) + 1 + strlen(DebugStrings[1]) + 1 + strlen(DebugStrings[2]) + 1;
+ di->m_type_name = strlen(DebugStrings[0]) + 1 + strlen(DebugStrings[1]) + 1 + strlen(DebugStrings[2]) + 1 + strlen(DebugStrings[3]) + 1;
+
+ /* zero end marker */
+ buf.MemPtr[buf.MemSize-1] = 0;
+ return true;
+}
+
+/* Build the DWARF lookup section */
+bool NotifyGdb::BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t die_offset)
+{
+ uint32_t length = sizeof(DwarfPubHeader) + sizeof(uint32_t) + strlen(name) + 1 + sizeof(uint32_t);
+
+ buf.MemSize = length;
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ DwarfPubHeader* header = reinterpret_cast<DwarfPubHeader*>(buf.MemPtr.GetValue());
+ header->m_length = length - sizeof(uint32_t);
+ header->m_version = 2;
+ header->m_debug_info_off = 0;
+ header->m_debug_info_len = size;
+ *reinterpret_cast<uint32_t*>(buf.MemPtr + sizeof(DwarfPubHeader)) = die_offset;
+ strcpy(buf.MemPtr + sizeof(DwarfPubHeader) + sizeof(uint32_t), name);
+ *reinterpret_cast<uint32_t*>(buf.MemPtr + length - sizeof(uint32_t)) = 0;
+
+ return true;
+}
+
+/* Build ELF .strtab section */
+bool NotifyGdb::BuildStringTableSection(MemBuf& buf)
+{
+ int len = 0;
+ for (int i = 0; i < sizeof(SymbolNames) / sizeof(SymbolNames[0]); ++i)
+ len += strlen(SymbolNames[i]) + 1;
+ len++; // end table with zero-length string
+
+ buf.MemSize = len;
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+ if (buf.MemPtr == nullptr)
+ return false;
+ char* ptr = buf.MemPtr;
+ for (int i = 0; i < sizeof(SymbolNames) / sizeof(SymbolNames[0]); ++i)
+ {
+ strcpy(ptr, SymbolNames[i]);
+ ptr += strlen(SymbolNames[i]) + 1;
+ }
+ buf.MemPtr[buf.MemSize-1] = 0;
+
+ return true;
+}
+
+/* Build ELF .symtab section */
+bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize)
+{
+ buf.MemSize = 2 * sizeof(Elf_Sym);
+ buf.MemPtr = new (nothrow) char[buf.MemSize];
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ Elf_Sym *sym = reinterpret_cast<Elf_Sym*>(buf.MemPtr.GetValue());
+ sym->st_name = 0;
+ sym->st_info = 0;
+ sym->st_other = 0;
+ sym->st_value = 0;
+ sym->st_size = 0;
+ sym->st_shndx = SHN_UNDEF;
+
+ sym++;
+ //sym = reinterpret_cast<Elf_Sym*>(buf.MemPtr.GetValue() + sizeof(Elf_Sym));
+ sym->st_name = 1;
+ sym->setBindingAndType(STB_GLOBAL, STT_FUNC);
+ sym->st_other = 0;
+#ifdef _TARGET_ARM_
+ sym->st_value = 1; // for THUMB code
+#else
+ sym->st_value = 0;
+#endif
+ sym->st_shndx = 1; // .text section index
+ sym->st_size = codeSize;
+ return true;
+}
+
+/* Build ELF string section */
+bool NotifyGdb::BuildSectionNameTable(MemBuf& buf)
+{
+ uint32_t totalLength = 0;
+
+ /* calculate total size */
+ for (int i = 0; i < SectionNamesCount; ++i)
+ {
+ totalLength += strlen(SectionNames[i]) + 1;
+ }
+
+ buf.MemSize = totalLength;
+ buf.MemPtr = new (nothrow) char[totalLength];
+ if (buf.MemPtr == nullptr)
+ return false;
+
+ /* copy strings */
+ char* bufPtr = buf.MemPtr;
+ for (int i = 0; i < SectionNamesCount; ++i)
+ {
+ strcpy(bufPtr, SectionNames[i]);
+ bufPtr += strlen(SectionNames[i]) + 1;
+ }
+
+ return true;
+}
+
+/* Build the ELF section headers table */
+bool NotifyGdb::BuildSectionTable(MemBuf& buf)
+{
+ Elf_Shdr* sectionHeaders = new (nothrow) Elf_Shdr[SectionNamesCount - 1];
+ Elf_Shdr* pSh = sectionHeaders;
+
+ if (sectionHeaders == nullptr)
+ {
+ return false;
+ }
+
+ /* NULL entry */
+ pSh->sh_name = 0;
+ pSh->sh_type = SHT_NULL;
+ pSh->sh_flags = 0;
+ pSh->sh_addr = 0;
+ pSh->sh_offset = 0;
+ pSh->sh_size = 0;
+ pSh->sh_link = SHN_UNDEF;
+ pSh->sh_info = 0;
+ pSh->sh_addralign = 0;
+ pSh->sh_entsize = 0;
+
+ ++pSh;
+ /* fill section header data */
+ uint32_t sectNameOffset = 1;
+ for (int i = 1; i < SectionNamesCount - 1; ++i, ++pSh)
+ {
+ pSh->sh_name = sectNameOffset;
+ sectNameOffset += strlen(SectionNames[i]) + 1;
+ pSh->sh_type = Sections[i].m_type;
+ pSh->sh_flags = Sections[i].m_flags;
+ pSh->sh_addr = 0;
+ pSh->sh_offset = 0;
+ pSh->sh_size = 0;
+ pSh->sh_link = SHN_UNDEF;
+ pSh->sh_info = 0;
+ pSh->sh_addralign = 1;
+ if (strcmp(SectionNames[i], ".symtab") == 0)
+ pSh->sh_entsize = sizeof(Elf_Sym);
+ else
+ pSh->sh_entsize = 0;
+ }
+
+ buf.MemPtr = reinterpret_cast<char*>(sectionHeaders);
+ buf.MemSize = sizeof(Elf_Shdr) * (SectionNamesCount - 1);
+ return true;
+}
+
+/* Build the ELF header */
+bool NotifyGdb::BuildELFHeader(MemBuf& buf)
+{
+ Elf_Ehdr* header = new (nothrow) Elf_Ehdr;
+ buf.MemPtr = reinterpret_cast<char*>(header);
+ buf.MemSize = sizeof(Elf_Ehdr);
+
+ if (header == nullptr)
+ return false;
+
+ return true;
+
+}
+
+/* Split full path name into directory & file anmes */
+void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const char*& fileName)
+{
+ char* pSlash = strrchr(path, '/');
+
+ if (pSlash != nullptr)
+ {
+ *pSlash = 0;
+ fileName = ++pSlash;
+ pathName = path;
+ }
+ else
+ {
+ fileName = path;
+ pathName = nullptr;
+ }
+}
+
+/* LEB128 for 32-bit unsigned integer */
+int NotifyGdb::Leb128Encode(uint32_t num, char* buf, int size)
+{
+ int i = 0;
+
+ do
+ {
+ uint8_t byte = num & 0x7F;
+ if (i >= size)
+ break;
+ num >>= 7;
+ if (num != 0)
+ byte |= 0x80;
+ buf[i++] = byte;
+ }
+ while (num != 0);
+
+ return i;
+}
+
+/* LEB128 for 32-bit signed integer */
+int NotifyGdb::Leb128Encode(int32_t num, char* buf, int size)
+{
+ int i = 0;
+ bool hasMore = true, isNegative = num < 0;
+
+ while (hasMore && i < size)
+ {
+ uint8_t byte = num & 0x7F;
+ num >>= 7;
+
+ if ((num == 0 && (byte & 0x40) == 0) || (num == -1 && (byte & 0x40) == 0x40))
+ hasMore = false;
+ else
+ byte |= 0x80;
+ buf[i++] = byte;
+ }
+
+ return i;
+}
+
+#ifdef _DEBUG
+void NotifyGdb::DumpElf(const char* methodName, const MemBuf& elfFile)
+{
+ char dump[1024];
+ strcpy(dump, "./");
+ strcat(dump, methodName);
+ strcat(dump, ".o");
+ FILE *f = fopen(dump, "wb");
+ fwrite(elfFile.MemPtr, sizeof(char),elfFile.MemSize, f);
+ fclose(f);
+}
+#endif
+
+/* ELF 32bit header */
+Elf32_Ehdr::Elf32_Ehdr()
+{
+ e_ident[EI_MAG0] = ElfMagic[0];
+ e_ident[EI_MAG1] = ElfMagic[1];
+ e_ident[EI_MAG2] = ElfMagic[2];
+ e_ident[EI_MAG3] = ElfMagic[3];
+ e_ident[EI_CLASS] = ELFCLASS32;
+ e_ident[EI_DATA] = ELFDATA2LSB;
+ e_ident[EI_VERSION] = EV_CURRENT;
+ e_ident[EI_OSABI] = ELFOSABI_NONE;
+ e_ident[EI_ABIVERSION] = 0;
+ for (int i = EI_PAD; i < EI_NIDENT; ++i)
+ e_ident[i] = 0;
+
+ e_type = ET_REL;
+#if defined(_TARGET_X86_)
+ e_machine = EM_386;
+#elif defined(_TARGET_ARM_)
+ e_machine = EM_ARM;
+#endif
+ e_flags = 0;
+ e_version = 1;
+ e_entry = 0;
+ e_phoff = 0;
+ e_ehsize = sizeof(Elf32_Ehdr);
+ e_phentsize = 0;
+ e_phnum = 0;
+}
+
+/* ELF 64bit header */
+Elf64_Ehdr::Elf64_Ehdr()
+{
+ e_ident[EI_MAG0] = ElfMagic[0];
+ e_ident[EI_MAG1] = ElfMagic[1];
+ e_ident[EI_MAG2] = ElfMagic[2];
+ e_ident[EI_MAG3] = ElfMagic[3];
+ e_ident[EI_CLASS] = ELFCLASS64;
+ e_ident[EI_DATA] = ELFDATA2LSB;
+ e_ident[EI_VERSION] = EV_CURRENT;
+ e_ident[EI_OSABI] = ELFOSABI_NONE;
+ e_ident[EI_ABIVERSION] = 0;
+ for (int i = EI_PAD; i < EI_NIDENT; ++i)
+ e_ident[i] = 0;
+
+ e_type = ET_REL;
+#if defined(_TARGET_AMD64_)
+ e_machine = EM_X86_64;
+#elif defined(_TARGET_ARM64_)
+ e_machine = EM_AARCH64;
+#endif
+ e_flags = 0;
+ e_version = 1;
+ e_entry = 0;
+ e_phoff = 0;
+ e_ehsize = sizeof(Elf64_Ehdr);
+ e_phentsize = 0;
+ e_phnum = 0;
+}