// 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.h // // // Header file for GDB JIT interface implemenation. // //***************************************************************************** #ifndef __GDBJIT_H__ #define __GDBJIT_H__ #include #include "typekey.h" #include "typestring.h" #include "method.hpp" #include "dbginterface.h" #include "../inc/llvm/ELF.h" #include "../inc/llvm/Dwarf.h" #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) typedef Elf32_Ehdr Elf_Ehdr; typedef Elf32_Shdr Elf_Shdr; typedef Elf32_Sym Elf_Sym; const uint16_t DW_FORM_size = DW_FORM_data4; #define ADDRESS_SIZE 4 #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) typedef Elf64_Ehdr Elf_Ehdr; typedef Elf64_Shdr Elf_Shdr; typedef Elf64_Sym Elf_Sym; const uint16_t DW_FORM_size = DW_FORM_data8; #define ADDRESS_SIZE 8 #else #error "Target is not supported" #endif static constexpr const int CorElementTypeToDWEncoding[] = { /* ELEMENT_TYPE_END */ 0, /* ELEMENT_TYPE_VOID */ DW_ATE_address, /* ELEMENT_TYPE_BOOLEAN */ DW_ATE_boolean, /* ELEMENT_TYPE_CHAR */ DW_ATE_UTF, /* ELEMENT_TYPE_I1 */ DW_ATE_signed, /* ELEMENT_TYPE_U1 */ DW_ATE_unsigned, /* ELEMENT_TYPE_I2 */ DW_ATE_signed, /* ELEMENT_TYPE_U2 */ DW_ATE_unsigned, /* ELEMENT_TYPE_I4 */ DW_ATE_signed, /* ELEMENT_TYPE_U4 */ DW_ATE_unsigned, /* ELEMENT_TYPE_I8 */ DW_ATE_signed, /* ELEMENT_TYPE_U8 */ DW_ATE_unsigned, /* ELEMENT_TYPE_R4 */ DW_ATE_float, /* ELEMENT_TYPE_R8 */ DW_ATE_float, /* ELEMENT_TYPE_STRING */ DW_ATE_address, /* ELEMENT_TYPE_PTR */ DW_ATE_address, /* ELEMENT_TYPE_BYREF */ DW_ATE_address, /* ELEMENT_TYPE_VALUETYPE */ DW_ATE_address, /* ELEMENT_TYPE_CLASS */ DW_ATE_address, /* ELEMENT_TYPE_VAR */ DW_ATE_address, /* ELEMENT_TYPE_ARRAY */ DW_ATE_address, /* ELEMENT_TYPE_GENERICINST */ DW_ATE_address, /* ELEMENT_TYPE_TYPEDBYREF */ DW_ATE_address, /* SKIP 17 */ DW_ATE_address, /* ELEMENT_TYPE_I */ DW_ATE_signed, /* ELEMENT_TYPE_U */ DW_ATE_unsigned, /* SKIP 1a */ DW_ATE_address, /* ELEMENT_TYPE_FNPTR */ DW_ATE_address, /* ELEMENT_TYPE_OBJECT */ DW_ATE_address, /* ELEMENT_TYPE_SZARRAY */ DW_ATE_address, /* ELEMENT_TYPE_MVAR */ DW_ATE_address, /* ELEMENT_TYPE_CMOD_REQD */ DW_ATE_address, /* ELEMENT_TYPE_CMOD_OPT */ DW_ATE_address, /* ELEMENT_TYPE_INTERNAL */ DW_ATE_address, /* ELEMENT_TYPE_MAX */ DW_ATE_address, }; struct __attribute__((packed)) DwarfCompUnit { uint32_t m_length; uint16_t m_version; uint32_t m_abbrev_offset; uint8_t m_addr_size; }; struct __attribute__((packed)) DwarfPubHeader { uint32_t m_length; uint16_t m_version; uint32_t m_debug_info_off; uint32_t m_debug_info_len; }; #define DW_LNS_MAX DW_LNS_set_isa struct __attribute__((packed)) DwarfLineNumHeader { uint32_t m_length; uint16_t m_version; uint32_t m_hdr_length; uint8_t m_min_instr_len; uint8_t m_def_is_stmt; int8_t m_line_base; uint8_t m_line_range; uint8_t m_opcode_base; uint8_t m_std_num_arg[DW_LNS_MAX]; }; const ULONG32 HiddenLine = 0x00feefee; struct SymbolsInfo { int lineNumber, ilOffset, nativeOffset, fileIndex; char fileName[2*MAX_PATH_FNAME]; ICorDebugInfo::SourceTypes source; }; class DwarfDumpable { public: DwarfDumpable() : m_base_ptr(nullptr), m_is_visited(false) { } // writes all string literals this type needs to ptr virtual void DumpStrings(char* ptr, int& offset) = 0; virtual void DumpDebugInfo(char* ptr, int& offset) = 0; virtual ~DwarfDumpable() {} char *m_base_ptr; bool m_is_visited; }; class LocalsInfo { public: struct Scope { int ilStartOffset; int ilEndOffset; }; int size; NewArrayHolder< NewArrayHolder > localsName; NewArrayHolder localsScope; ULONG32 countVars; NewArrayHolder vars; }; class TypeMember; class TypeInfoBase : public DwarfDumpable { public: TypeInfoBase(TypeHandle typeHandle) : m_type_name(nullptr), m_type_name_offset(0), m_type_size(0), m_type_offset(0), typeHandle(typeHandle), typeKey(typeHandle.GetTypeKey()) { } virtual void DumpStrings(char* ptr, int& offset) override; void CalculateName(); void SetTypeHandle(TypeHandle handle); TypeHandle GetTypeHandle(); TypeKey* GetTypeKey(); NewArrayHolder m_type_name; int m_type_name_offset; ULONG m_type_size; int m_type_offset; private: TypeHandle typeHandle; TypeKey typeKey; }; class TypeDefInfo : public DwarfDumpable { public: TypeDefInfo(char *typedef_name,int typedef_type): m_typedef_name(typedef_name), m_typedef_type(typedef_type), m_typedef_type_offset(0) {} void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; NewArrayHolder m_typedef_name; int m_typedef_type; int m_typedef_type_offset; int m_typedef_name_offset; }; class PrimitiveTypeInfo: public TypeInfoBase { public: PrimitiveTypeInfo(TypeHandle typeHandle); void DumpDebugInfo(char* ptr, int& offset) override; void DumpStrings(char* ptr, int& offset) override; int m_type_encoding; NewHolder m_typedef_info; }; class RefTypeInfo: public TypeInfoBase { public: RefTypeInfo(TypeHandle typeHandle, TypeInfoBase *value_type) : TypeInfoBase(typeHandle), m_value_type(value_type) { m_type_size = sizeof(TADDR); CalculateName(); } void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; TypeInfoBase *m_value_type; }; class NamedRefTypeInfo: public RefTypeInfo { public: NamedRefTypeInfo(TypeHandle typeHandle, TypeInfoBase *value_type) : RefTypeInfo(typeHandle, value_type), m_value_type_storage(value_type) { } void DumpDebugInfo(char* ptr, int& offset) override; NewHolder m_value_type_storage; }; class FunctionMemberPtrArrayHolder; class ArrayTypeInfo; class ClassTypeInfo: public TypeInfoBase { public: ClassTypeInfo(TypeHandle typeHandle, int num_members, FunctionMemberPtrArrayHolder &method); void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; int m_num_members; NewArrayHolder members; TypeInfoBase* m_parent; FunctionMemberPtrArrayHolder &m_method; NewHolder m_array_type; NewHolder m_array_bounds_type; }; class TypeMember: public DwarfDumpable { public: TypeMember() : m_member_name(nullptr), m_member_name_offset(0), m_member_offset(0), m_static_member_address(0), m_member_type(nullptr) { } void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; void DumpStaticDebugInfo(char* ptr, int& offset); NewArrayHolder m_member_name; int m_member_name_offset; int m_member_offset; TADDR m_static_member_address; TypeInfoBase *m_member_type; }; class ArrayTypeInfo: public TypeInfoBase { public: ArrayTypeInfo(TypeHandle typeHandle, int count, TypeInfoBase* elemType) : TypeInfoBase(typeHandle), m_count(count), m_elem_type(elemType) { } void DumpDebugInfo(char* ptr, int& offset) override; int m_count; TypeInfoBase *m_elem_type; }; class VarDebugInfo: public DwarfDumpable { public: VarDebugInfo(int abbrev) : m_var_name(nullptr), m_var_abbrev(abbrev), m_var_name_offset(0), m_il_index(0), m_native_offset(0), m_var_type(nullptr), m_low_pc(0), m_high_pc(0) { } VarDebugInfo() : m_var_name(nullptr), m_var_abbrev(6), m_var_name_offset(0), m_il_index(0), m_native_offset(0), m_var_type(nullptr), m_low_pc(0), m_high_pc(0) { } void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; NewArrayHolder m_var_name; int m_var_abbrev; int m_var_name_offset; int m_il_index; int m_native_offset; TypeInfoBase *m_var_type; uintptr_t m_low_pc; uintptr_t m_high_pc; }; struct Elf_Symbol; class Elf_Builder; class DebugStringsCU; class NotifyGdb { public: class FileTableBuilder; static void Initialize(); static void MethodPrepared(MethodDesc* methodDescPtr); static void MethodPitched(MethodDesc* methodDescPtr); template class DeleteValuesOnDestructSHashTraits : public PARENT_TRAITS { public: static inline void OnDestructPerEntryCleanupAction(typename PARENT_TRAITS::element_t e) { delete e.Value(); } static const bool s_DestructPerEntryCleanupAction = true; }; template class TypeKeyHashTraits : public DefaultSHashTraits< KeyValuePair > { public: // explicitly declare local typedefs for these traits types, otherwise // the compiler may get confused typedef typename DefaultSHashTraits< KeyValuePair >::element_t element_t; typedef typename DefaultSHashTraits< KeyValuePair >::count_t count_t; typedef TypeKey* key_t; static key_t GetKey(element_t e) { LIMITED_METHOD_CONTRACT; return e.Key(); } static BOOL Equals(key_t k1, key_t k2) { LIMITED_METHOD_CONTRACT; return k1->Equals(k2); } static count_t Hash(key_t k) { LIMITED_METHOD_CONTRACT; return k->ComputeHash(); } static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t(key_t(),VALUE()); } static const element_t Deleted() { LIMITED_METHOD_CONTRACT; return element_t(key_t(-1), VALUE()); } static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e.Key() == key_t(); } static bool IsDeleted(const element_t &e) { return e.Key() == key_t(-1); } }; typedef MapSHash>> TK_TypeInfoMap; typedef TK_TypeInfoMap* PTK_TypeInfoMap; typedef SetSHash< TADDR, NoRemoveSHashTraits < NonDacAwareSHashTraits< SetSHashTraits > > > AddrSet; private: struct MemBuf { NewArrayHolder MemPtr; unsigned MemSize; MemBuf() : MemPtr(0), MemSize(0) {} void Resize(unsigned newSize) { if (newSize == 0) { MemPtr = nullptr; MemSize = 0; return; } char *tmp = new char [newSize]; memmove(tmp, MemPtr.GetValue(), newSize < MemSize ? newSize : MemSize); MemPtr = tmp; MemSize = newSize; } }; static void OnMethodPrepared(MethodDesc* methodDescPtr); #ifdef FEATURE_GDBJIT_FRAME static bool EmitFrameInfo(Elf_Builder &, PCODE pCode, TADDR codeSzie); #endif // FEATURE_GDBJIT_FRAME #ifdef FEATURE_GDBJIT_SYMTAB static bool EmitSymtab(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize); #endif // FEATURE_GDBJIT_SYMTAB static bool EmitDebugInfo(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize); static bool BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, int methodCount, NewArrayHolder &symbolNames, int symbolCount, unsigned int thunkIndexBase); static bool BuildStringTableSection(MemBuf& strTab, NewArrayHolder &symbolNames, int symbolCount); static bool BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method, DebugStringsCU &debugStringsCU); static bool BuildDebugAbbrev(MemBuf& buf); static bool BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method, DebugStringsCU &debugStringsCU); static bool BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t dieOffset); static bool BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines, const char * &cuPath); static bool BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines, const char * &cuPath); static bool BuildLineProg(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines); static void IssueSetAddress(char*& ptr, PCODE addr); static void IssueEndOfSequence(char*& ptr); static void IssueSimpleCommand(char*& ptr, uint8_t command); static void IssueParamCommand(char*& ptr, uint8_t command, char* param, int param_len); static const char* SplitFilename(const char* path); static bool CollectCalledMethods(CalledMethod* pCM, TADDR nativeCode, FunctionMemberPtrArrayHolder &method, NewArrayHolder &symbolNames, int &symbolCount); }; class FunctionMember: public TypeMember { public: FunctionMember(MethodDesc *md, int num_locals, int num_args) : TypeMember(), md(md), m_file(1), m_line(1), m_sub_low_pc(0), m_sub_high_pc(0), m_sub_loc(), m_num_args(num_args), m_num_locals(num_locals), m_num_vars(num_args + num_locals), m_entry_offset(0), vars(new VarDebugInfo[m_num_vars]), lines(NULL), nlines(0), m_linkage_name_offset(0), dumped(false) { m_sub_loc[0] = 1; #if defined(_TARGET_AMD64_) m_sub_loc[1] = DW_OP_reg6; #elif defined(_TARGET_X86_) m_sub_loc[1] = DW_OP_reg5; #elif defined(_TARGET_ARM64_) m_sub_loc[1] = DW_OP_reg29; #elif defined(_TARGET_ARM_) m_sub_loc[1] = DW_OP_reg11; #else #error Unsupported platform! #endif } void DumpStrings(char* ptr, int& offset) override; void DumpDebugInfo(char* ptr, int& offset) override; void DumpTryCatchDebugInfo(char* ptr, int& offset); HRESULT GetLocalsDebugInfo(NotifyGdb::PTK_TypeInfoMap pTypeMap, LocalsInfo& locals, int startNativeOffset, FunctionMemberPtrArrayHolder &method); BOOL IsDumped() { return dumped; } MethodDesc *md; uint8_t m_file, m_line; uintptr_t m_sub_low_pc, m_sub_high_pc; uint8_t m_sub_loc[2]; uint8_t m_num_args; uint8_t m_num_locals; uint16_t m_num_vars; int m_entry_offset; NewArrayHolder vars; SymbolsInfo* lines; unsigned nlines; int m_linkage_name_offset; private: int GetArgsAndLocalsLen(); void MangleName(char *buf, int &buf_offset, const char *name); void DumpMangledNamespaceAndMethod(char *buf, int &offset, const char *nspace, const char *mname); void DumpLinkageName(char* ptr, int& offset); bool GetBlockInNativeCode(int blockILOffset, int blockILLen, TADDR *startOffset, TADDR *endOffset); void DumpTryCatchBlock(char* ptr, int& offset, int ilOffset, int ilLen, int abbrev); void DumpVarsWithScopes(char* ptr, int& offset); BOOL dumped; }; #endif // #ifndef __GDBJIT_H__