diff options
Diffstat (limited to 'src/vm/gdbjit.cpp')
-rw-r--r-- | src/vm/gdbjit.cpp | 2054 |
1 files changed, 1784 insertions, 270 deletions
diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp index 9f9c116820..8e728839d6 100644 --- a/src/vm/gdbjit.cpp +++ b/src/vm/gdbjit.cpp @@ -11,16 +11,323 @@ //***************************************************************************** #include "common.h" +#include "formattype.h" #include "gdbjit.h" #include "gdbjithelpers.h" -struct DebuggerILToNativeMap +TypeInfoBase* +GetTypeInfoFromTypeHandle(TypeHandle typeHandle, NotifyGdb::PTK_TypeInfoMap pTypeMap) { - ULONG ilOffset; - ULONG nativeStartOffset; - ULONG nativeEndOffset; - ICorDebugInfo::SourceTypes source; -}; + TypeInfoBase *typeInfo = nullptr; + TypeKey key = typeHandle.GetTypeKey(); + PTR_MethodTable pMT = typeHandle.GetMethodTable(); + + if (pTypeMap->Lookup(&key, &typeInfo)) + { + return typeInfo; + } + + CorElementType corType = typeHandle.GetSignatureCorElementType(); + switch (corType) + { + case ELEMENT_TYPE_VOID: + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + typeInfo = new (nothrow) PrimitiveTypeInfo(typeHandle, CorElementTypeToDWEncoding[corType]); + if (typeInfo == nullptr) + return nullptr; + + typeInfo->m_type_size = CorTypeInfo::Size(corType); + + break; + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + ApproxFieldDescIterator fieldDescIterator(pMT, + pMT->IsString() ? ApproxFieldDescIterator::INSTANCE_FIELDS : ApproxFieldDescIterator::ALL_FIELDS); + ULONG cFields = fieldDescIterator.Count(); + + typeInfo = new (nothrow) ClassTypeInfo(typeHandle, cFields); + + if (typeInfo == nullptr) + return nullptr; + + typeInfo->m_type_size = typeHandle.AsMethodTable()->GetClass()->GetSize(); + + RefTypeInfo* refTypeInfo = nullptr; + if (!typeHandle.IsValueType()) + { + // name the type + refTypeInfo = new (nothrow) RefTypeInfo(typeHandle, typeInfo); + if (refTypeInfo == nullptr) + { + return nullptr; + } + refTypeInfo->m_type_size = sizeof(TADDR); + refTypeInfo->m_value_type = typeInfo; + refTypeInfo->CalculateName(); + + pTypeMap->Add(refTypeInfo->GetTypeKey(), refTypeInfo); + } + + pTypeMap->Add(typeInfo->GetTypeKey(), typeInfo); + typeInfo->CalculateName(); + + // + // Now fill in the array + // + FieldDesc *pField; + + for (ULONG i = 0; i < cFields; i++) + { + pField = fieldDescIterator.Next(); + ClassTypeInfo *info = static_cast<ClassTypeInfo*>(typeInfo); + + LPCUTF8 szName = pField->GetName(); + info->members[i].m_member_name = new char[strlen(szName) + 1]; + strcpy(info->members[i].m_member_name, szName); + if (!pField->IsStatic()) + { + info->members[i].m_member_offset = (ULONG)pField->GetOffset(); + if (!typeHandle.IsValueType()) + info->members[i].m_member_offset += Object::GetOffsetOfFirstField(); + } + else + { + PTR_BYTE base = 0; + MethodTable* pMT = pField->GetEnclosingMethodTable(); + base = pField->GetBase(); + + // TODO: add support of generics with static fields + if (pField->IsRVA() || !pMT->IsDynamicStatics()) + { + PTR_VOID pAddress = pField->GetStaticAddressHandle((PTR_VOID)dac_cast<TADDR>(base)); + info->members[i].m_static_member_address = dac_cast<TADDR>(pAddress); + } + } + + info->members[i].m_member_type = + GetTypeInfoFromTypeHandle(pField->GetExactFieldType(typeHandle), pTypeMap); + + // handle the System.String case: + // coerce type of the second field into array type + if (pMT->IsString() && i == 1) + { + TypeInfoBase* elemTypeInfo = info->members[1].m_member_type; + TypeInfoBase* arrayTypeInfo = new (nothrow) ArrayTypeInfo(typeHandle.MakeSZArray(), 0, elemTypeInfo); + if (arrayTypeInfo == nullptr) + return nullptr; + info->members[1].m_member_type = arrayTypeInfo; + } + } + if (refTypeInfo) + return refTypeInfo; + else + return typeInfo; + } + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_BYREF: + { + TypeInfoBase* valTypeInfo = GetTypeInfoFromTypeHandle(typeHandle.GetTypeParam(), pTypeMap); + typeInfo = new (nothrow) RefTypeInfo(typeHandle, valTypeInfo); + if (typeInfo == nullptr) + return nullptr; + typeInfo->m_type_size = sizeof(TADDR); + typeInfo->m_type_offset = valTypeInfo->m_type_offset; + break; + } + case ELEMENT_TYPE_ARRAY: + case ELEMENT_TYPE_SZARRAY: + { + typeInfo = new (nothrow) ClassTypeInfo(typeHandle, 2); + if (typeInfo == nullptr) + return nullptr; + typeInfo->m_type_size = pMT->GetClass()->GetSize(); + + typeInfo->CalculateName(); + RefTypeInfo *refTypeInfo = new (nothrow) RefTypeInfo(typeHandle, typeInfo); + if (refTypeInfo == nullptr) + { + return nullptr; + } + refTypeInfo->m_type_size = sizeof(TADDR); + refTypeInfo->m_value_type = typeInfo; + refTypeInfo->CalculateName(); + + pTypeMap->Add(refTypeInfo->GetTypeKey(), refTypeInfo); + + TypeInfoBase* lengthTypeInfo = GetTypeInfoFromTypeHandle( + TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_I4)), pTypeMap); + + TypeInfoBase* valTypeInfo = GetTypeInfoFromTypeHandle(typeHandle.GetTypeParam(), pTypeMap); + TypeInfoBase* arrayTypeInfo = new (nothrow) ArrayTypeInfo(typeHandle, 0, valTypeInfo); + if (arrayTypeInfo == nullptr) + return nullptr; + + ClassTypeInfo *info = static_cast<ClassTypeInfo*>(typeInfo); + + info->members[0].m_member_name = new (nothrow) char[16]; + strcpy(info->members[0].m_member_name, "m_NumComponents"); + info->members[0].m_member_offset = ArrayBase::GetOffsetOfNumComponents(); + info->members[0].m_member_type = lengthTypeInfo; + info->members[0].m_member_type->m_type_size = sizeof(DWORD); + + info->members[1].m_member_name = new (nothrow) char[7]; + strcpy(info->members[1].m_member_name, "m_Data"); + info->members[1].m_member_offset = ArrayBase::GetDataPtrOffset(pMT); + info->members[1].m_member_type = arrayTypeInfo; + info->members[1].m_member_type->m_type_size = sizeof(TADDR); + + return refTypeInfo; + } + default: + ASSERT(0 && "not implemented"); + break; + } + // name the type + if (corType == ELEMENT_TYPE_CHAR) + { + typeInfo->m_type_name = new char[9]; + strcpy(typeInfo->m_type_name, "char16_t"); + } + else + { + typeInfo->CalculateName(); + } + pTypeMap->Add(typeInfo->GetTypeKey(), typeInfo); + return typeInfo; +} + +TypeInfoBase* GetArgTypeInfo(MethodDesc* MethodDescPtr, + NotifyGdb::PTK_TypeInfoMap pTypeMap, + unsigned ilIndex) +{ + MetaSig sig(MethodDescPtr); + TypeHandle th; + if (ilIndex == 0) + { + th = sig.GetRetTypeHandleNT(); + } + else + { + while (--ilIndex) + sig.SkipArg(); + + sig.NextArg(); + th = sig.GetLastTypeHandleNT(); + } + return GetTypeInfoFromTypeHandle(th, pTypeMap); +} + +TypeInfoBase* GetLocalTypeInfo(MethodDesc *MethodDescPtr, + NotifyGdb::PTK_TypeInfoMap pTypeMap, + unsigned ilIndex) +{ + COR_ILMETHOD_DECODER method(MethodDescPtr->GetILHeader()); + if (method.GetLocalVarSigTok()) + { + DWORD cbSigLen; + PCCOR_SIGNATURE pComSig; + + if (FAILED(MethodDescPtr->GetMDImport()->GetSigFromToken(method.GetLocalVarSigTok(), &cbSigLen, &pComSig))) + { + printf("\nInvalid record"); + return nullptr; + } + + _ASSERTE(*pComSig == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG); + + SigTypeContext typeContext(MethodDescPtr, TypeHandle()); + MetaSig sig(pComSig, cbSigLen, MethodDescPtr->GetModule(), &typeContext, MetaSig::sigLocalVars); + if (ilIndex > 0) + { + while (ilIndex--) + sig.SkipArg(); + } + sig.NextArg(); + TypeHandle th = sig.GetLastTypeHandleNT(); + return GetTypeInfoFromTypeHandle(th, pTypeMap); + } + return nullptr; +} + +HRESULT GetArgNameByILIndex(MethodDesc* MethodDescPtr, unsigned index, LPSTR ¶mName) +{ + IMDInternalImport* mdImport = MethodDescPtr->GetMDImport(); + mdParamDef paramToken; + USHORT seq; + DWORD attr; + HRESULT status; + + // Param indexing is 1-based. + ULONG32 mdIndex = index + 1; + + MetaSig sig(MethodDescPtr); + if (sig.HasThis()) + { + mdIndex--; + } + status = mdImport->FindParamOfMethod(MethodDescPtr->GetMemberDef(), mdIndex, ¶mToken); + if (status == S_OK) + { + LPCSTR name; + status = mdImport->GetParamDefProps(paramToken, &seq, &attr, &name); + paramName = new char[strlen(name) + 1]; + strcpy(paramName, name); + } + return status; +} + +// Copy-pasted from src/debug/di/module.cpp +HRESULT FindNativeInfoInILVariable(DWORD dwIndex, + SIZE_T ip, + ICorDebugInfo::NativeVarInfo** nativeInfoList, + unsigned int nativeInfoCount, + ICorDebugInfo::NativeVarInfo** ppNativeInfo) +{ + _ASSERTE(ppNativeInfo != NULL); + *ppNativeInfo = NULL; + int lastGoodOne = -1; + for (unsigned int i = 0; i < (unsigned)nativeInfoCount; i++) + { + if ((*nativeInfoList)[i].varNumber == dwIndex) + { + if ((lastGoodOne == -1) || ((*nativeInfoList)[lastGoodOne].startOffset < (*nativeInfoList)[i].startOffset)) + { + lastGoodOne = i; + } + + if (((*nativeInfoList)[i].startOffset <= ip) && + ((*nativeInfoList)[i].endOffset > ip)) + { + *ppNativeInfo = &((*nativeInfoList)[i]); + + return S_OK; + } + } + } + + if ((lastGoodOne > -1) && ((*nativeInfoList)[lastGoodOne].endOffset == ip)) + { + *ppNativeInfo = &((*nativeInfoList)[lastGoodOne]); + return S_OK; + } + + return CORDBG_E_IL_VAR_NOT_AVAILABLE; +} + BYTE* DebugInfoStoreNew(void * pData, size_t cBytes) { return new (nothrow) BYTE[cBytes]; @@ -30,7 +337,9 @@ BYTE* DebugInfoStoreNew(void * pData, size_t cBytes) HRESULT GetMethodNativeMap(MethodDesc* methodDesc, ULONG32* numMap, - DebuggerILToNativeMap** map) + DebuggerILToNativeMap** map, + ULONG32* pcVars, + ICorDebugInfo::NativeVarInfo** ppVars) { // Use the DebugInfoStore to get IL->Native maps. // It doesn't matter whether we're jitted, ngenned etc. @@ -48,8 +357,8 @@ GetMethodNativeMap(MethodDesc* methodDesc, NULL, // allocator &countMapCopy, &mapCopy, - NULL, - NULL); + pcVars, + ppVars); if (!success) { @@ -83,9 +392,62 @@ GetMethodNativeMap(MethodDesc* methodDesc, return S_OK; } +HRESULT FunctionMember::GetLocalsDebugInfo(NotifyGdb::PTK_TypeInfoMap pTypeMap, + LocalsInfo& locals, + int startNativeOffset) +{ + + ICorDebugInfo::NativeVarInfo* nativeVar = NULL; + int thisOffs = 0; + if (!md->IsStatic()) + { + thisOffs = 1; + } + + int i; + for (i = 0; i < m_num_args - thisOffs; i++) + { + if (FindNativeInfoInILVariable(i + thisOffs, startNativeOffset, &locals.pVars, locals.countVars, &nativeVar) == S_OK) + { + vars[i + thisOffs].m_var_type = GetArgTypeInfo(md, pTypeMap, i + 1); + GetArgNameByILIndex(md, i + thisOffs, vars[i + thisOffs].m_var_name); + vars[i + thisOffs].m_il_index = i; + vars[i + thisOffs].m_native_offset = nativeVar->loc.vlStk.vlsOffset; + vars[i + thisOffs].m_var_abbrev = 6; + } + } + //Add info about 'this' as first argument + if (thisOffs == 1) + { + if (FindNativeInfoInILVariable(0, startNativeOffset, &locals.pVars, locals.countVars, &nativeVar) == S_OK) + { + vars[0].m_var_type = GetTypeInfoFromTypeHandle(TypeHandle(md->GetMethodTable()), pTypeMap); + vars[0].m_var_name = new char[strlen("this") + 1]; + strcpy(vars[0].m_var_name, "this"); + vars[0].m_il_index = 0; + vars[0].m_native_offset = nativeVar->loc.vlStk.vlsOffset; + vars[0].m_var_abbrev = 13; + } + i++; + } + for (; i < m_num_vars; i++) + { + if (FindNativeInfoInILVariable( + i, startNativeOffset, &locals.pVars, locals.countVars, &nativeVar) == S_OK) + { + vars[i].m_var_type = GetLocalTypeInfo(md, pTypeMap, i - m_num_args); + vars[i].m_var_name = new char[strlen(locals.localsName[i - m_num_args]) + 1]; + strcpy(vars[i].m_var_name, locals.localsName[i - m_num_args]); + vars[i].m_il_index = i - m_num_args; + vars[i].m_native_offset = nativeVar->loc.vlStk.vlsOffset; + vars[i].m_var_abbrev = 5; + } + } + return S_OK; +} /* Get mapping of IL offsets to source line numbers */ HRESULT -GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned int &symInfoLen) +GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned int &symInfoLen, LocalsInfo &locals) { DebuggerILToNativeMap* map = NULL; @@ -93,8 +455,8 @@ GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned i if (!getInfoForMethodDelegate) return E_FAIL; - - if (GetMethodNativeMap(MethodDescPtr, &numMap, &map) != S_OK) + + if (GetMethodNativeMap(MethodDescPtr, &numMap, &map, &locals.countVars, &locals.pVars) != S_OK) return E_FAIL; const Module* mod = MethodDescPtr->GetMethodTable()->GetModule(); @@ -105,48 +467,125 @@ GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned i StackScratchBuffer scratch; const char* szModName = modName.GetUTF8(scratch); - MethodDebugInfo* methodDebugInfo = new (nothrow) MethodDebugInfo(); - if (methodDebugInfo == nullptr) - return E_OUTOFMEMORY; + MethodDebugInfo methodDebugInfo; - methodDebugInfo->points = (SequencePointInfo*) CoTaskMemAlloc(sizeof(SequencePointInfo) * numMap); - if (methodDebugInfo->points == nullptr) + methodDebugInfo.points = (SequencePointInfo*) CoTaskMemAlloc(sizeof(SequencePointInfo) * numMap); + if (methodDebugInfo.points == nullptr) return E_OUTOFMEMORY; - methodDebugInfo->size = numMap; + methodDebugInfo.size = numMap; - if (getInfoForMethodDelegate(szModName, MethodDescPtr->GetMemberDef(), *methodDebugInfo) == FALSE) + if (getInfoForMethodDelegate(szModName, MethodDescPtr->GetMemberDef(), methodDebugInfo) == FALSE) return E_FAIL; - symInfoLen = methodDebugInfo->size; - *symInfo = new (nothrow) SymbolsInfo[symInfoLen]; + symInfoLen = numMap; + *symInfo = new (nothrow) SymbolsInfo[numMap]; if (*symInfo == nullptr) return E_FAIL; + locals.size = methodDebugInfo.localsSize; + locals.localsName = new (nothrow) char *[locals.size]; + if (locals.localsName == nullptr) + return E_FAIL; + + for (ULONG32 i = 0; i < locals.size; i++) + { + size_t sizeRequired = WideCharToMultiByte(CP_UTF8, 0, methodDebugInfo.locals[i], -1, NULL, 0, NULL, NULL); + locals.localsName[i] = new (nothrow) char[sizeRequired]; - for (ULONG32 i = 0; i < symInfoLen; i++) + int len = WideCharToMultiByte( + CP_UTF8, 0, methodDebugInfo.locals[i], -1, locals.localsName[i], sizeRequired, NULL, NULL); + } + + for (ULONG32 j = 0; j < numMap; j++) { - for (ULONG32 j = 0; j < numMap; j++) + SymbolsInfo& s = (*symInfo)[j]; + + if (j == 0) { + s.fileName[0] = 0; + s.lineNumber = 0; + s.fileIndex = 0; + } else { + s = (*symInfo)[j - 1]; + } + s.nativeOffset = map[j].nativeStartOffset; + s.ilOffset = map[j].ilOffset; + s.source = map[j].source; + s.lineNumber = 0; + + for (ULONG32 i = 0; i < methodDebugInfo.size; i++) { - if (methodDebugInfo->points[i].ilOffset == map[j].ilOffset) - { - SymbolsInfo& s = (*symInfo)[i]; - const SequencePointInfo& sp = methodDebugInfo->points[i]; + const SequencePointInfo& sp = methodDebugInfo.points[i]; - s.nativeOffset = map[j].nativeStartOffset; - s.ilOffset = map[j].ilOffset; + if (methodDebugInfo.points[i].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; + break; } } } - CoTaskMemFree(methodDebugInfo->points); + CoTaskMemFree(methodDebugInfo.points); return S_OK; } +/* LEB128 for 32-bit unsigned integer */ +int 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 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; +} + +int GetFrameLocation(int nativeOffset, char* bufVarLoc) +{ + char cnvBuf[16] = {0}; + int len = Leb128Encode(static_cast<int32_t>(nativeOffset), cnvBuf, sizeof(cnvBuf)); + bufVarLoc[0] = len + 1; + bufVarLoc[1] = DW_OP_fbreg; + for (int j = 0; j < len; j++) + { + bufVarLoc[j + 2] = cnvBuf[j]; + } + + return len + 2; // We add '2' because first 2 bytes contain length of expression and DW_OP_fbreg operation. +} + // GDB JIT interface typedef enum { @@ -185,11 +624,24 @@ struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; /* Predefined section names */ const char* SectionNames[] = { - "", ".text", ".shstrtab", ".debug_str", ".debug_abbrev", ".debug_info", - ".debug_pubnames", ".debug_pubtypes", ".debug_line", ".symtab", ".strtab", "" + "", + ".text", + ".shstrtab", + ".debug_str", + ".debug_abbrev", + ".debug_info", + ".debug_pubnames", + ".debug_pubtypes", + ".debug_line", + ".symtab", + ".strtab" + /* After the last (.strtab) section zero or more .thunk_* sections are generated. + + Each .thunk_* section contains a single .thunk_#. + These symbols are mapped to methods (or trampolines) called by currently compiled method. */ }; -const int SectionNamesCount = sizeof(SectionNames) / sizeof(SectionNames[0]); +const int SectionNamesCount = sizeof(SectionNames) / sizeof(SectionNames[0]); // Does not include .thunk_* sections /* Static data for section headers */ struct SectionHeader { @@ -207,11 +659,12 @@ struct SectionHeader { {SHT_PROGBITS, 0}, {SHT_SYMTAB, 0}, {SHT_STRTAB, 0}, + {SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR} }; /* Static data for .debug_str section */ const char* DebugStrings[] = { - "CoreCLR", "" /* module name */, "" /* module path */, "" /* method name */, "int" + "CoreCLR", "" /* module name */, "" /* module path */ }; const int DebugStringCount = sizeof(DebugStrings) / sizeof(DebugStrings[0]); @@ -221,11 +674,95 @@ 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, + + 2, 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, + + 3, DW_TAG_typedef, 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, + DW_AT_type, DW_FORM_ref4, 0, 0, + + 4, DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, DW_AT_linkage_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, + DW_AT_low_pc, DW_FORM_addr, DW_AT_high_pc, +#if defined(_TARGET_AMD64_) + DW_FORM_data8, +#elif defined(_TARGET_ARM_) + DW_FORM_data4, +#else +#error Unsupported platform! +#endif + DW_AT_frame_base, DW_FORM_exprloc, 0, 0, + + 5, DW_TAG_variable, 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_location, DW_FORM_exprloc, 0, 0, + + 6, DW_TAG_formal_parameter, 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_location, DW_FORM_exprloc, 0, 0, + + 7, DW_TAG_class_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, DW_AT_byte_size, DW_FORM_data1, 0, 0, + + 8, DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, DW_AT_type, DW_FORM_ref4, DW_AT_data_member_location, DW_FORM_data4, 0, 0, + + 9, DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4, DW_AT_byte_size, DW_FORM_data1, 0, 0, + + 10, DW_TAG_array_type, DW_CHILDREN_yes, + DW_AT_type, DW_FORM_ref4, 0, 0, + + 11, DW_TAG_subrange_type, DW_CHILDREN_no, + DW_AT_upper_bound, DW_FORM_exprloc, 0, 0, + + 12, DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, DW_AT_linkage_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, + DW_AT_low_pc, DW_FORM_addr, DW_AT_high_pc, +#if defined(_TARGET_AMD64_) + DW_FORM_data8, +#elif defined(_TARGET_ARM_) + DW_FORM_data4, +#else +#error Unsupported platform! +#endif + DW_AT_frame_base, DW_FORM_exprloc, DW_AT_object_pointer, DW_FORM_ref4, 0, 0, + + 13, DW_TAG_formal_parameter, 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_location, DW_FORM_exprloc, DW_AT_artificial, DW_FORM_flag_present, 0, 0, + + 14, DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, DW_AT_type, DW_FORM_ref4, DW_AT_external, DW_FORM_flag_present, 0, 0, + + 15, DW_TAG_variable, DW_CHILDREN_no, DW_AT_specification, DW_FORM_ref4, DW_AT_location, DW_FORM_exprloc, + 0, 0, + + 16, DW_TAG_try_block, DW_CHILDREN_no, + DW_AT_low_pc, DW_FORM_addr, DW_AT_high_pc, +#if defined(_TARGET_AMD64_) + DW_FORM_data8, +#elif defined(_TARGET_ARM_) + DW_FORM_data4, +#else +#error Unsupported platform! +#endif + 0, 0, + + 17, DW_TAG_catch_block, DW_CHILDREN_no, + DW_AT_low_pc, DW_FORM_addr, DW_AT_high_pc, +#if defined(_TARGET_AMD64_) + DW_FORM_data8, +#elif defined(_TARGET_ARM_) + DW_FORM_data4, +#else +#error Unsupported platform! +#endif + 0, 0, + 0 }; @@ -241,53 +778,781 @@ DwarfLineNumHeader LineNumHeader = { }; /* Static data for .debug_info */ -struct __attribute__((packed)) DebugInfo +struct __attribute__((packed)) DebugInfoCU { uint8_t m_cu_abbrev; uint32_t m_prod_off; uint16_t m_lang; uint32_t m_cu_name; uint32_t m_line_num; - +} debugInfoCU = { + 1, 0, DW_LANG_C89, 0, 0 +}; + +struct __attribute__((packed)) DebugInfoTryCatchSub +{ + uint8_t m_sub_abbrev; +#if defined(_TARGET_AMD64_) + uint64_t m_sub_low_pc, m_sub_high_pc; +#elif defined(_TARGET_ARM_) + uint32_t m_sub_low_pc, m_sub_high_pc; +#else +#error Unsupported platform! +#endif +}; + +struct __attribute__((packed)) DebugInfoSub +{ uint8_t m_sub_abbrev; uint32_t m_sub_name; + uint32_t m_linkage_name; uint8_t m_file, m_line; uint32_t m_sub_type; - +#if defined(_TARGET_AMD64_) + uint64_t m_sub_low_pc, m_sub_high_pc; +#elif defined(_TARGET_ARM_) + uint32_t m_sub_low_pc, m_sub_high_pc; +#else +#error Unsupported platform! +#endif + uint8_t m_sub_loc[2]; +}; + +struct __attribute__((packed)) DebugInfoSubMember +{ + DebugInfoSub sub; + uint32_t m_obj_ptr; +}; + +// Holder for array of pointers to FunctionMember objects +class FunctionMemberPtrArrayHolder : public NewArrayHolder<FunctionMember*> +{ +private: + int m_cElements; + + void DeleteElements() + { + for (int i = 0; i < m_cElements; i++) + { + delete this->m_value[i]; + } + } + +public: + FunctionMemberPtrArrayHolder() : + NewArrayHolder<FunctionMember*>(), + m_cElements(0) + { + } + + bool Alloc(int cElements) + { + FunctionMember** value = new (nothrow) FunctionMember*[cElements]; + if (value == nullptr) + return false; + + for (int i = 0; i < cElements; i++) + { + value[i] = nullptr; + } + + // Clean previous elements + DeleteElements(); + + NewArrayHolder<FunctionMember*>::operator=(value); + m_cElements = cElements; + return true; + } + + int GetCount() const + { + return m_cElements; + } + + ~FunctionMemberPtrArrayHolder() + { + DeleteElements(); + } +}; + +static FunctionMemberPtrArrayHolder method; + +struct __attribute__((packed)) DebugInfoType +{ 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 }; +struct __attribute__((packed)) DebugInfoVar +{ + uint8_t m_var_abbrev; + uint32_t m_var_name; + uint8_t m_var_file, m_var_line; + uint32_t m_var_type; +}; + +struct __attribute__((packed)) DebugInfoClassType +{ + uint8_t m_type_abbrev; + uint32_t m_type_name; + uint8_t m_byte_size; +}; + +struct __attribute__((packed)) DebugInfoClassMember +{ + uint8_t m_member_abbrev; + uint32_t m_member_name; + uint32_t m_member_type; +}; + +struct __attribute__((packed)) DebugInfoStaticMember +{ + uint8_t m_member_abbrev; + uint32_t m_member_specification; +}; + + +struct __attribute__((packed)) DebugInfoRefType +{ + uint8_t m_type_abbrev; + uint32_t m_ref_type; + uint8_t m_byte_size; +}; + +struct __attribute__((packed)) DebugInfoArrayType +{ + uint8_t m_abbrev; + uint32_t m_type; +}; + +void TypeInfoBase::DumpStrings(char* ptr, int& offset) +{ + if (ptr != nullptr) + { + strcpy(ptr + offset, m_type_name); + m_type_name_offset = offset; + } + offset += strlen(m_type_name) + 1; +} + +void TypeInfoBase::CalculateName() +{ + // name the type + SString sName; + typeHandle.GetName(sName); + StackScratchBuffer buffer; + const UTF8 *utf8 = sName.GetUTF8(buffer); + m_type_name = new char[strlen(utf8) + 1]; + strcpy(m_type_name, utf8); +} + +void TypeInfoBase::SetTypeHandle(TypeHandle handle) +{ + typeHandle = handle; + typeKey = handle.GetTypeKey(); +} + +TypeHandle TypeInfoBase::GetTypeHandle() +{ + return typeHandle; +} + +TypeKey* TypeInfoBase::GetTypeKey() +{ + return &typeKey; +} + +void PrimitiveTypeInfo::DumpDebugInfo(char* ptr, int& offset) +{ + if (m_type_offset != 0) + { + return; + } + + if (ptr != nullptr) + { + DebugInfoType bufType; + bufType.m_type_abbrev = 2; + bufType.m_type_name = m_type_name_offset; + bufType.m_encoding = m_type_encoding; + bufType.m_byte_size = m_type_size; + + memcpy(ptr + offset, + &bufType, + sizeof(DebugInfoType)); + m_type_offset = offset; + } + + offset += sizeof(DebugInfoType); +} + +ClassTypeInfo::ClassTypeInfo(TypeHandle typeHandle, int num_members) + : TypeInfoBase(typeHandle), + m_num_members(num_members), + members(new TypeMember[num_members]) +{ +} + +ClassTypeInfo::~ClassTypeInfo() +{ + if (members != nullptr && m_num_members > 0) + { + delete[] members; + } +} + +void TypeMember::DumpStrings(char* ptr, int& offset) +{ + if (ptr != nullptr) + { + strcpy(ptr + offset, m_member_name); + m_member_name_offset = offset; + } + offset += strlen(m_member_name) + 1; +} + +void TypeMember::DumpDebugInfo(char* ptr, int& offset) +{ + if (ptr != nullptr) + { + DebugInfoClassMember memberEntry; + + if (m_static_member_address == 0) + memberEntry.m_member_abbrev = 8; + else + { + memberEntry.m_member_abbrev = 14; + m_member_offset = offset; + } + memberEntry.m_member_name = m_member_name_offset; + memberEntry.m_member_type = m_member_type->m_type_offset; + + memcpy(ptr + offset, &memberEntry, sizeof(DebugInfoClassMember)); + if (m_static_member_address == 0) + memcpy(ptr + offset + sizeof(DebugInfoClassMember), &m_member_offset, sizeof(m_member_offset)); + } + offset += sizeof(DebugInfoClassMember); + if (m_static_member_address == 0) + offset += sizeof(m_member_offset); +} + +void TypeMember::DumpStaticDebugInfo(char* ptr, int& offset) +{ + const int ptrSize = sizeof(TADDR); + if (ptr != nullptr) + { + DebugInfoStaticMember memberEntry; + + memberEntry.m_member_abbrev = 15; + memberEntry.m_member_specification = m_member_offset; + memcpy(ptr + offset, &memberEntry, sizeof(DebugInfoStaticMember)); + + char buf[ptrSize + 2] = {0}; + buf[0] = ptrSize + 1; + buf[1] = DW_OP_addr; + + for (int i = 0; i < ptrSize; i++) + { + buf[i + 2] = m_static_member_address >> (i * 8); + } + + memcpy(ptr + offset + sizeof(DebugInfoStaticMember), &buf, ptrSize + 2); + } + offset += sizeof(DebugInfoStaticMember); + offset += ptrSize + 2; +} + +void FunctionMember::MangleName(char *buf, int &buf_offset, const char *name) +{ + int name_length = strlen(name); + + char tmp[20]; + int tmp_len = sprintf_s(tmp, _countof(tmp), "%i", name_length); + if (tmp_len <= 0) + return; + + if (buf) + strncpy(buf + buf_offset, tmp, tmp_len); + buf_offset += tmp_len; + + if (buf) + { + for (int i = 0; i < name_length; i++) + { + char c = name[i]; + bool valid = (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9'); + *(buf + buf_offset + i) = valid ? c : '_'; + } + } + buf_offset += name_length; +} + +void FunctionMember::DumpMangledNamespaceAndMethod(char *buf, int &offset, const char *nspace, const char *mname) +{ + static const char *begin_mangled = "_ZN"; + static const char *end_mangled = "Ev"; + static const int begin_mangled_len = strlen(begin_mangled); + static const int end_mangled_len = strlen(end_mangled); + + if (buf) + strncpy(buf + offset, begin_mangled, begin_mangled_len); + offset += begin_mangled_len; + + MangleName(buf, offset, nspace); + MangleName(buf, offset, mname); + + if (buf) + strncpy(buf + offset, end_mangled, end_mangled_len); + offset += end_mangled_len; + + if (buf) + buf[offset] = '\0'; + ++offset; +} + +void FunctionMember::DumpLinkageName(char* ptr, int& offset) +{ + SString namespaceOrClassName; + SString methodName; + + md->GetMethodInfoNoSig(namespaceOrClassName, methodName); + SString utf8namespaceOrClassName; + SString utf8methodName; + namespaceOrClassName.ConvertToUTF8(utf8namespaceOrClassName); + methodName.ConvertToUTF8(utf8methodName); + + const char *nspace = utf8namespaceOrClassName.GetUTF8NoConvert(); + const char *mname = utf8methodName.GetUTF8NoConvert(); + + if (!nspace || !mname) + { + m_linkage_name_offset = 0; + return; + } + + m_linkage_name_offset = offset; + DumpMangledNamespaceAndMethod(ptr, offset, nspace, mname); +} + +void FunctionMember::DumpStrings(char* ptr, int& offset) +{ + TypeMember::DumpStrings(ptr, offset); + + for (int i = 0; i < m_num_vars; ++i) + { + vars[i].DumpStrings(ptr, offset); + } + + DumpLinkageName(ptr, offset); +} + +bool FunctionMember::GetBlockInNativeCode(int blockILOffset, int blockILLen, TADDR *startOffset, TADDR *endOffset) +{ + PCODE pCode = md->GetNativeCode(); + + const int blockILEnd = blockILOffset + blockILLen; + + *startOffset = 0; + *endOffset = 0; + + bool inBlock = false; + + for (int i = 0; i < nlines; ++i) + { + TADDR nativeOffset = lines[i].nativeOffset + pCode; + + // Limit block search to current function addresses + if (nativeOffset < m_sub_low_pc) + continue; + if (nativeOffset >= m_sub_low_pc + m_sub_high_pc) + break; + + // Skip invalid IL offsets + switch(lines[i].ilOffset) + { + case ICorDebugInfo::PROLOG: + case ICorDebugInfo::EPILOG: + case ICorDebugInfo::NO_MAPPING: + continue; + default: + break; + } + + // Check if current IL is within block + if (blockILOffset <= lines[i].ilOffset && lines[i].ilOffset < blockILEnd) + { + if (!inBlock) + { + *startOffset = lines[i].nativeOffset; + inBlock = true; + } + } + else + { + if (inBlock) + { + *endOffset = lines[i].nativeOffset; + inBlock = false; + break; + } + } + } + + if (inBlock) + { + *endOffset = m_sub_low_pc + m_sub_high_pc - pCode; + } + + return *endOffset != *startOffset; +} + +void FunctionMember::DumpTryCatchBlock(char* ptr, int& offset, int ilOffset, int ilLen, int abbrev) +{ + TADDR startOffset; + TADDR endOffset; + + if (!GetBlockInNativeCode(ilOffset, ilLen, &startOffset, &endOffset)) + return; + + if (ptr != nullptr) + { + DebugInfoTryCatchSub subEntry; + + subEntry.m_sub_abbrev = abbrev; + subEntry.m_sub_low_pc = md->GetNativeCode() + startOffset; + subEntry.m_sub_high_pc = endOffset - startOffset; + + memcpy(ptr + offset, &subEntry, sizeof(DebugInfoTryCatchSub)); + } + offset += sizeof(DebugInfoTryCatchSub); +} + +void FunctionMember::DumpTryCatchDebugInfo(char* ptr, int& offset) +{ + if (!md) + return; + + COR_ILMETHOD *pHeader = md->GetILHeader(); + COR_ILMETHOD_DECODER header(pHeader); + + unsigned ehCount = header.EHCount(); + + for (unsigned e = 0; e < ehCount; e++) + { + IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff; + const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; + + ehInfo = header.EH->EHClause(e, &ehBuff); + + DumpTryCatchBlock(ptr, offset, ehInfo->TryOffset, ehInfo->TryLength, 16); + DumpTryCatchBlock(ptr, offset, ehInfo->HandlerOffset, ehInfo->HandlerLength, 17); + } +} + +void FunctionMember::DumpDebugInfo(char* ptr, int& offset) +{ + if (ptr != nullptr) + { + DebugInfoSub subEntry; + + subEntry.m_sub_abbrev = 4; + subEntry.m_sub_name = m_member_name_offset; + subEntry.m_linkage_name = m_linkage_name_offset; + subEntry.m_file = m_file; + subEntry.m_line = m_line; + subEntry.m_sub_type = m_member_type->m_type_offset; + subEntry.m_sub_low_pc = m_sub_low_pc; + subEntry.m_sub_high_pc = m_sub_high_pc; + subEntry.m_sub_loc[0] = m_sub_loc[0]; + subEntry.m_sub_loc[1] = m_sub_loc[1]; + + if (!md->IsStatic()) + { + DebugInfoSubMember subMemberEntry; + subEntry.m_sub_abbrev = 12; + subMemberEntry.sub = subEntry; + subMemberEntry.m_obj_ptr = offset+sizeof(DebugInfoSubMember); + memcpy(ptr + offset, &subMemberEntry, sizeof(DebugInfoSubMember)); + } + else + { + memcpy(ptr + offset, &subEntry, sizeof(DebugInfoSub)); + } + m_entry_offset = offset; + dumped = true; + } + + if (!md->IsStatic()) + { + offset += sizeof(DebugInfoSubMember); + } + else + { + offset += sizeof(DebugInfoSub); + } + for (int i = 0; i < m_num_vars; ++i) + { + vars[i].DumpDebugInfo(ptr, offset); + } + + DumpTryCatchDebugInfo(ptr, offset); + + // terminate children + if (ptr != nullptr) + { + ptr[offset] = 0; + } + offset++; +} + +int FunctionMember::GetArgsAndLocalsLen() +{ + int locSize = 0; + char tmpBuf[16]; + + // Format for DWARF location expression: [expression length][operation][offset in SLEB128 encoding] + for (int i = 0; i < m_num_vars; i++) + { + locSize += 2; // First byte contains expression length, second byte contains operation (DW_OP_fbreg). + locSize += Leb128Encode(static_cast<int32_t>(vars[i].m_native_offset), tmpBuf, sizeof(tmpBuf)); + } + return locSize; +} + +void ClassTypeInfo::DumpStrings(char* ptr, int& offset) +{ + TypeInfoBase::DumpStrings(ptr, offset); + + for (int i = 0; i < m_num_members; ++i) + { + members[i].DumpStrings(ptr, offset); + } +} + +void RefTypeInfo::DumpStrings(char* ptr, int& offset) +{ + TypeInfoBase::DumpStrings(ptr, offset); + m_value_type->DumpStrings(ptr, offset); +} + +void RefTypeInfo::DumpDebugInfo(char* ptr, int& offset) +{ + if (m_type_offset != 0) + { + return; + } + m_type_offset = offset; + offset += sizeof(DebugInfoRefType); + m_value_type->DumpDebugInfo(ptr, offset); + if (ptr != nullptr) + { + DebugInfoRefType refType; + refType.m_type_abbrev = 9; + refType.m_ref_type = m_value_type->m_type_offset; + refType.m_byte_size = m_type_size; + memcpy(ptr + m_type_offset, &refType, sizeof(DebugInfoRefType)); + } + else + { + m_type_offset = 0; + } +} +void ClassTypeInfo::DumpDebugInfo(char* ptr, int& offset) +{ + if (m_type_offset != 0) + { + return; + } + // make sure that types of all members are dumped + for (int i = 0; i < m_num_members; ++i) + { + if (members[i].m_member_type->m_type_offset == 0 && members[i].m_member_type != this) + { + members[i].m_member_type->DumpDebugInfo(ptr, offset); + } + } + + if (ptr != nullptr) + { + DebugInfoClassType bufType; + bufType.m_type_abbrev = 7; + bufType.m_type_name = m_type_name_offset; + bufType.m_byte_size = m_type_size; + + memcpy(ptr + offset, &bufType, sizeof(DebugInfoClassType)); + m_type_offset = offset; + } + offset += sizeof(DebugInfoClassType); + + for (int i = 0; i < m_num_members; ++i) + { + members[i].DumpDebugInfo(ptr, offset); + } + + for (int i = 0; i < method.GetCount(); ++i) + { + if (method[i]->md->GetMethodTable() == GetTypeHandle().GetMethodTable()) + { + // our method is part of this class, we should dump it now before terminating members + method[i]->DumpDebugInfo(ptr, offset); + } + } + + // members terminator + if (ptr != nullptr) + { + ptr[offset] = 0; + } + offset++; + + for (int i = 0; i < m_num_members; ++i) + { + if (members[i].m_static_member_address != 0) + members[i].DumpStaticDebugInfo(ptr, offset); + } + +} + +void ArrayTypeInfo::DumpDebugInfo(char* ptr, int& offset) +{ + if (m_type_offset != 0) + { + return; + } + if (m_elem_type->m_type_offset == 0) + { + m_elem_type->DumpDebugInfo(ptr, offset); + } + if (ptr != nullptr) + { + DebugInfoArrayType arrType; + + arrType.m_abbrev = 10; // DW_TAG_array_type abbrev + arrType.m_type = m_elem_type->m_type_offset; + + memcpy(ptr + offset, &arrType, sizeof(DebugInfoArrayType)); + m_type_offset = offset; + } + offset += sizeof(DebugInfoArrayType); + + char tmp[16] = { 0 }; + int len = Leb128Encode(static_cast<int32_t>(m_count_offset), tmp, sizeof(tmp)); + if (ptr != nullptr) + { + char buf[64]; + buf[0] = 11; // DW_TAG_subrange_type abbrev + buf[1] = len + 3; + buf[2] = DW_OP_push_object_address; + buf[3] = DW_OP_plus_uconst; + for (int j = 0; j < len; j++) + { + buf[j + 4] = tmp[j]; + } + buf[len + 4] = DW_OP_deref; + + memcpy(ptr + offset, buf, len + 5); + } + offset += (len + 5); + + if (ptr != nullptr) + { + memset(ptr + offset, 0, 1); + } + offset += 1; +} + +void VarDebugInfo::DumpStrings(char *ptr, int& offset) +{ + if (ptr != nullptr) + { + strcpy(ptr + offset, m_var_name); + m_var_name_offset = offset; + } + offset += strlen(m_var_name) + 1; +} + +void VarDebugInfo::DumpDebugInfo(char* ptr, int& offset) +{ + char bufVarLoc[16]; + int len = GetFrameLocation(m_native_offset, bufVarLoc); + if (ptr != nullptr) + { + DebugInfoVar bufVar; + + bufVar.m_var_abbrev = m_var_abbrev; + bufVar.m_var_name = m_var_name_offset; + bufVar.m_var_file = 1; + bufVar.m_var_line = 1; + bufVar.m_var_type = m_var_type->m_type_offset; + memcpy(ptr + offset, &bufVar, sizeof(DebugInfoVar)); + memcpy(ptr + offset + sizeof(DebugInfoVar), bufVarLoc, len); + } + offset += sizeof(DebugInfoVar); + offset += len; +} + /* static data for symbol strings */ -const char* SymbolNames[] = { - "", "" +struct Elf_Symbol { + const char* m_name; + int m_off; + TADDR m_value; + int m_section, m_size; + bool m_releaseName; + Elf_Symbol() : m_name(nullptr), m_off(0), m_value(0), m_section(0), m_size(0), m_releaseName(false) {} + ~Elf_Symbol() + { + if (m_releaseName) + delete [] m_name; + } }; +static int countFuncs(const SymbolsInfo *lines, int nlines) +{ + int count = 0; + for (int i = 0; i < nlines; i++) { + if (lines[i].ilOffset == ICorDebugInfo::PROLOG) + { + count++; + } + } + return count; +} + +static int getNextPrologueIndex(int from, const SymbolsInfo *lines, int nlines) +{ + for (int i = from; i < nlines; ++i) { + if (lines[i].ilOffset == ICorDebugInfo::PROLOG) + { + return i; + } + } + return -1; +} + +int SymbolCount = 0; +NewArrayHolder<Elf_Symbol> SymbolNames; +NotifyGdb::AddrSet codeAddrs; /* 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; + LocalsInfo locals; /* 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 + + pCode = PCODEToPINSTR(pCode); /* Get module name */ const Module* mod = MethodDescPtr->GetMethodTable()->GetModule(); @@ -357,15 +1622,82 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) } if (isUserDebug == FALSE) + { return; + } + + NewHolder<TK_TypeInfoMap> pTypeMap = new TK_TypeInfoMap(); + + if (pTypeMap == nullptr) + { + return; + } /* Get debug info for method from portable PDB */ - HRESULT hr = GetDebugInfoFromPDB(MethodDescPtr, &symInfo, symInfoLen); + HRESULT hr = GetDebugInfoFromPDB(MethodDescPtr, &symInfo, symInfoLen, locals); if (FAILED(hr) || symInfoLen == 0) { return; } + int method_count = countFuncs(symInfo, symInfoLen); + if (!method.Alloc(method_count)) { + return; + } + + CodeHeader* pCH = (CodeHeader*)pCode - 1; + CalledMethod* pCalledMethods = reinterpret_cast<CalledMethod*>(pCH->GetCalledMethods()); + /* Collect addresses of thunks called by method */ + if (!CollectCalledMethods(pCalledMethods, (TADDR)MethodDescPtr->GetNativeCode())) + { + return; + } + pCH->SetCalledMethods(NULL); + + MetaSig sig(MethodDescPtr); + int nArgsCount = sig.NumFixedArgs(); + if (sig.HasThis()) + nArgsCount++; + + unsigned int firstLineIndex = 0; + for (;firstLineIndex < symInfoLen; firstLineIndex++) { + if (symInfo[firstLineIndex].lineNumber != 0 && symInfo[firstLineIndex].lineNumber != HiddenLine) break; + } + + if (firstLineIndex >= symInfoLen) + { + return; + } + + int start_index = getNextPrologueIndex(0, symInfo, symInfoLen); + + for (int method_index = 0; method_index < method.GetCount(); ++method_index) + { + method[method_index] = new FunctionMember(MethodDescPtr, locals.size, nArgsCount); + + int end_index = getNextPrologueIndex(start_index + 1, symInfo, symInfoLen); + + PCODE method_start = symInfo[start_index].nativeOffset; + TADDR method_size = end_index == -1 ? codeSize - method_start : symInfo[end_index].nativeOffset - method_start; + + // method return type + method[method_index]->m_member_type = GetArgTypeInfo(MethodDescPtr, pTypeMap, 0); + method[method_index]->GetLocalsDebugInfo(pTypeMap, locals, symInfo[firstLineIndex].nativeOffset); + method[method_index]->m_sub_low_pc = pCode + method_start; + method[method_index]->m_sub_high_pc = method_size; + size_t methodNameSize = strlen(methodName) + 10; + method[method_index]->m_member_name = new char[methodNameSize]; + if (method_index == 0) + sprintf_s(method[method_index]->m_member_name, methodNameSize, "%s", methodName); + else + sprintf_s(method[method_index]->m_member_name, methodNameSize, "%s_%i", methodName, method_index); + + // method's class + GetTypeInfoFromTypeHandle(TypeHandle(method[method_index]->md->GetMethodTable()), pTypeMap); + + start_index = end_index; + } + MemBuf elfHeader, sectHeaders, sectStr, sectSymTab, sectStrTab, dbgInfo, dbgAbbrev, dbgPubname, dbgPubType, dbgLine, dbgStr, elfFile; @@ -374,42 +1706,52 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) { return; } - + /* Build .debug_line section */ - if (!BuildLineTable(dbgLine, pCode, symInfo, symInfoLen)) + if (!BuildLineTable(dbgLine, pCode, codeSize, symInfo, symInfoLen)) { return; } DebugStrings[1] = szModuleFile; - DebugStrings[3] = methodName; /* Build .debug_str section */ - if (!BuildDebugStrings(dbgStr)) + if (!BuildDebugStrings(dbgStr, pTypeMap)) { return; } /* Build .debug_info section */ - if (!BuildDebugInfo(dbgInfo)) + if (!BuildDebugInfo(dbgInfo, pTypeMap, symInfo, symInfoLen)) { return; } - + + for (int i = 0; i < locals.size; i++) + { + delete[] locals.localsName[i]; + } /* Build .debug_pubname section */ - if (!BuildDebugPub(dbgPubname, methodName, dbgInfo.MemSize, 26)) + if (!BuildDebugPub(dbgPubname, methodName, dbgInfo.MemSize, 0x28)) { return; } /* Build debug_pubtype section */ - if (!BuildDebugPub(dbgPubType, "int", dbgInfo.MemSize, 37)) + if (!BuildDebugPub(dbgPubType, "int", dbgInfo.MemSize, 0x1a)) { return; } - + /* Build .strtab section */ - SymbolNames[1] = methodName; + SymbolNames[0].m_name = ""; + for (int i = 0; i < method.GetCount(); ++i) + { + SymbolNames[1 + i].m_name = method[i]->m_member_name; + SymbolNames[1 + i].m_value = method[i]->m_sub_low_pc; + SymbolNames[1 + i].m_section = 1; + SymbolNames[1 + i].m_size = method[i]->m_sub_high_pc; + } if (!BuildStringTableSection(sectStrTab)) { return; @@ -420,15 +1762,8 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) return; } - - /* Build section names section */ - if (!BuildSectionNameTable(sectStr)) - { - return; - } - - /* Build section headers table */ - if (!BuildSectionTable(sectHeaders)) + /* Build section headers table and section names table */ + if (!BuildSectionTables(sectHeaders, sectStr)) { return; } @@ -470,13 +1805,21 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) ++pShdr; // .symtab pShdr->sh_offset = offset; pShdr->sh_size = sectSymTab.MemSize; - pShdr->sh_link = 10; + pShdr->sh_link = GetSectionIndex(".strtab"); offset += sectSymTab.MemSize; ++pShdr; // .strtab pShdr->sh_offset = offset; pShdr->sh_size = sectStrTab.MemSize; offset += sectStrTab.MemSize; - + + // .thunks + for (int i = 1 + method.GetCount(); i < SymbolCount; i++) + { + ++pShdr; + pShdr->sh_addr = PCODEToPINSTR(SymbolNames[i].m_value); + pShdr->sh_size = 8; + } + /* Build ELF header */ if (!BuildELFHeader(elfHeader)) { @@ -493,8 +1836,9 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) #endif header->e_shoff = offset; header->e_shentsize = sizeof(Elf_Shdr); - header->e_shnum = SectionNamesCount - 1; - header->e_shstrndx = 2; + int thunks_count = SymbolCount - method.GetCount() - 1; + header->e_shnum = SectionNamesCount + thunks_count; + header->e_shstrndx = GetSectionIndex(".shstrtab"); /* Build ELF image in memory */ elfFile.MemSize = elfHeader.MemSize + sectStr.MemSize + dbgStr.MemSize + dbgAbbrev.MemSize + dbgInfo.MemSize + @@ -531,12 +1875,14 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) memcpy(elfFile.MemPtr + offset, sectHeaders.MemPtr, sectHeaders.MemSize); + elfFile.MemPtr.SuppressRelease(); + #ifdef GDBJIT_DUMPELF DumpElf(methodName, elfFile); -#endif - +#endif + /* Create GDB JIT structures */ - jit_code_entry* jit_symbols = new (nothrow) jit_code_entry; + NewHolder<jit_code_entry> jit_symbols = new (nothrow) jit_code_entry; if (jit_symbols == nullptr) { @@ -557,15 +1903,21 @@ void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) head->prev_entry = jit_symbols; } + jit_symbols.SuppressRelease(); + /* 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) { + static const int textSectionIndex = GetSectionIndex(".text"); + + if (textSectionIndex < 0) + return; + PCODE pCode = MethodDescPtr->GetNativeCode(); if (pCode == NULL) @@ -579,7 +1931,7 @@ void NotifyGdb::MethodDropped(MethodDesc* MethodDescPtr) 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 + pShdr += textSectionIndex; // bump to .text section if (pShdr->sh_addr == pCode) { /* Notify the debugger */ @@ -602,7 +1954,7 @@ void NotifyGdb::MethodDropped(MethodDesc* MethodDescPtr) } /* Build the DWARF .debug_line section */ -bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines) +bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines) { MemBuf fileTable, lineProg; @@ -610,7 +1962,7 @@ bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, if (!BuildFileTable(fileTable, lines, nlines)) return false; /* Build line info program */ - if (!BuildLineProg(lineProg, startAddr, lines, nlines)) + if (!BuildLineProg(lineProg, startAddr, codeSize, lines, nlines)) { return false; } @@ -640,7 +1992,7 @@ bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, /* Buid the source files table for DWARF source line info */ bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines) { - const char** files = nullptr; + NewArrayHolder<const char*> files = nullptr; unsigned nfiles = 0; /* GetValue file names and replace them with indices in file table */ @@ -649,6 +2001,8 @@ bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines) return false; for (unsigned i = 0; i < nlines; ++i) { + if (lines[i].fileName[0] == 0) + continue; const char *filePath, *fileName; SplitPathname(lines[i].fileName, filePath, fileName); @@ -686,7 +2040,6 @@ bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines) if (buf.MemPtr == nullptr) { - delete[] files; return false; } @@ -704,7 +2057,6 @@ bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines) // final zero byte *ptr = 0; - delete[] files; return true; } @@ -742,44 +2094,77 @@ void NotifyGdb::IssueParamCommand(char*& ptr, uint8_t command, char* param, int } } -/* 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) +static void fixLineMapping(SymbolsInfo* lines, unsigned nlines) { - unsigned opcode = (line_shift - DWARF_LINE_BASE) + addr_shift * DWARF_LINE_RANGE + DWARF_OPCODE_BASE; - - return opcode < 255; + // Fix EPILOGUE line mapping + int prevLine = 0; + for (int i = 0; i < nlines; ++i) + { + if (lines[i].lineNumber == HiddenLine) + continue; + if (lines[i].ilOffset == ICorDebugInfo::PROLOG) // will be fixed in next step + { + prevLine = 0; + } + else + { + if (lines[i].lineNumber == 0) + { + lines[i].lineNumber = prevLine; + } + else + { + prevLine = lines[i].lineNumber; + } + } + } + // Fix PROLOGUE line mapping + prevLine = lines[nlines - 1].lineNumber; + for (int i = nlines - 1; i >= 0; --i) + { + if (lines[i].lineNumber == HiddenLine) + continue; + if (lines[i].lineNumber == 0) + lines[i].lineNumber = prevLine; + else + prevLine = lines[i].lineNumber; + } + // Skip HiddenLines + for (int i = 0; i < nlines; ++i) + { + if (lines[i].lineNumber == HiddenLine) + { + lines[i].lineNumber = 0; + if (i + 1 < nlines && lines[i + 1].ilOffset == ICorDebugInfo::NO_MAPPING) + lines[i + 1].lineNumber = 0; + } + } } /* Build program for DWARF source line section */ -bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines) +bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, TADDR codeSize, 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 */ + /* reserve memory assuming worst case: set address, advance line command, set proglogue/epilogue and copy for each line */ + buf.MemSize = + 6 /* set file command */ + nlines * 6 /* advance line commands */ - + nlines * (4 + ADDRESS_SIZE) /* 1 extended + 1 special command */ + + nlines * (3 + ADDRESS_SIZE) /* set address commands */ + + nlines * 1 /* set prologue end or epilogue begin commands */ + + nlines * 1 /* copy commands */ + + 6 /* advance PC 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; - + + fixLineMapping(lines, nlines); + + int prevLine = 1, prevFile = 0; + for (int i = 0; i < nlines; ++i) { /* different source file */ @@ -789,26 +2174,38 @@ bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, 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)) - { + + // GCC don't use the is_prologue_end flag to mark the first instruction after the prologue. + // Instead of it it is issueing a line table entry for the first instruction of the prologue + // and one for the first instruction after the prologue. + // We do not want to confuse the debugger so we have to avoid adding a line in such case. + if (i > 0 && lines[i - 1].nativeOffset == lines[i].nativeOffset) + continue; + + IssueSetAddress(ptr, startAddr + lines[i].nativeOffset); + + if (lines[i].lineNumber != prevLine) { 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; + + if (lines[i].ilOffset == ICorDebugInfo::EPILOG) + IssueSimpleCommand(ptr, DW_LNS_set_epilogue_begin); + else if (i > 0 && lines[i - 1].ilOffset == ICorDebugInfo::PROLOG) + IssueSimpleCommand(ptr, DW_LNS_set_prologue_end); + + IssueParamCommand(ptr, DW_LNS_copy, NULL, 0); } - + + int lastAddr = nlines > 0 ? lines[nlines - 1].nativeOffset : 0; + + // Advance PC to the end of function + if (lastAddr < codeSize) { + int len = Leb128Encode(static_cast<uint32_t>(codeSize - lastAddr), cnv_buf, sizeof(cnv_buf)); + IssueParamCommand(ptr, DW_LNS_advance_pc, cnv_buf, len); + } + IssueEndOfSequence(ptr); buf.MemSize = ptr - buf.MemPtr; @@ -816,16 +2213,31 @@ bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, } /* Build the DWARF .debug_str section */ -bool NotifyGdb::BuildDebugStrings(MemBuf& buf) +bool NotifyGdb::BuildDebugStrings(MemBuf& buf, PTK_TypeInfoMap pTypeMap) { - uint32_t totalLength = 0; - + int totalLength = 0; + /* calculate total section size */ for (int i = 0; i < DebugStringCount; ++i) { totalLength += strlen(DebugStrings[i]) + 1; } - + + for (int i = 0; i < method.GetCount(); ++i) + { + method[i]->DumpStrings(nullptr, totalLength); + } + + { + auto iter = pTypeMap->Begin(); + while (iter != pTypeMap->End()) + { + TypeInfoBase *typeInfo = iter->Value(); + typeInfo->DumpStrings(nullptr, totalLength); + iter++; + } + } + buf.MemSize = totalLength; buf.MemPtr = new (nothrow) char[totalLength]; @@ -834,12 +2246,28 @@ bool NotifyGdb::BuildDebugStrings(MemBuf& buf) /* copy strings */ char* bufPtr = buf.MemPtr; + int offset = 0; for (int i = 0; i < DebugStringCount; ++i) { - strcpy(bufPtr, DebugStrings[i]); - bufPtr += strlen(DebugStrings[i]) + 1; + strcpy(bufPtr + offset, DebugStrings[i]); + offset += strlen(DebugStrings[i]) + 1; } - + + for (int i = 0; i < method.GetCount(); ++i) + { + method[i]->DumpStrings(bufPtr, offset); + } + + { + auto iter = pTypeMap->Begin(); + while (iter != pTypeMap->End()) + { + TypeInfoBase *typeInfo = iter->Value(); + typeInfo->DumpStrings(bufPtr, offset); + iter++; + } + } + return true; } @@ -848,7 +2276,7 @@ bool NotifyGdb::BuildDebugAbbrev(MemBuf& buf) { buf.MemPtr = new (nothrow) char[AbbrevTableSize]; buf.MemSize = AbbrevTableSize; - + if (buf.MemPtr == nullptr) return false; @@ -857,31 +2285,79 @@ bool NotifyGdb::BuildDebugAbbrev(MemBuf& buf) } /* Build tge DWARF .debug_info section */ -bool NotifyGdb::BuildDebugInfo(MemBuf& buf) +bool NotifyGdb::BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, SymbolsInfo* lines, unsigned nlines) { - buf.MemSize = sizeof(DwarfCompUnit) + sizeof(DebugInfo) + 1; + int totalTypeVarSubSize = 0; + { + auto iter = pTypeMap->Begin(); + while (iter != pTypeMap->End()) + { + TypeInfoBase *typeInfo = iter->Value(); + typeInfo->DumpDebugInfo(nullptr, totalTypeVarSubSize); + iter++; + } + } + + for (int i = 0; i < method.GetCount(); ++i) + { + method[i]->lines = lines; + method[i]->nlines = nlines; + method[i]->DumpDebugInfo(nullptr, totalTypeVarSubSize); + } + // Drop pointers to lines when exiting current scope + struct DropMethodLines + { + ~DropMethodLines() + { + for (int i = 0; i < method.GetCount(); ++i) + { + method[i]->lines = nullptr; + method[i]->nlines = 0; + } + } + } dropMethodLines; + + //int locSize = GetArgsAndLocalsLen(argsDebug, argsDebugSize, localsDebug, localsDebugSize); + buf.MemSize = sizeof(DwarfCompUnit) + sizeof(DebugInfoCU) + totalTypeVarSubSize + 2; buf.MemPtr = new (nothrow) char[buf.MemSize]; if (buf.MemPtr == nullptr) return false; - + int offset = 0; /* 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; + offset += sizeof(DwarfCompUnit); + DebugInfoCU* diCU = + reinterpret_cast<DebugInfoCU*>(buf.MemPtr + offset); + memcpy(buf.MemPtr + offset, &debugInfoCU, sizeof(DebugInfoCU)); + offset += sizeof(DebugInfoCU); + diCU->m_prod_off = 0; + diCU->m_cu_name = strlen(DebugStrings[0]) + 1; + { + auto iter = pTypeMap->Begin(); + while (iter != pTypeMap->End()) + { + TypeInfoBase *typeInfo = iter->Value(); + typeInfo->DumpDebugInfo(buf.MemPtr, offset); + iter++; + } + } + for (int i = 0; i < method.GetCount(); ++i) + { + if (!method[i]->IsDumped()) + { + method[i]->DumpDebugInfo(buf.MemPtr, offset); + } + else + { + method[i]->DumpDebugInfo(buf.MemPtr, method[i]->m_entry_offset); + } + } + memset(buf.MemPtr + offset, 0, buf.MemSize - offset); return true; } @@ -908,12 +2384,60 @@ bool NotifyGdb::BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint return true; } +/* Store addresses and names of the called methods into symbol table */ +bool NotifyGdb::CollectCalledMethods(CalledMethod* pCalledMethods, TADDR nativeCode) +{ + AddrSet tmpCodeAddrs; + + if (!codeAddrs.Contains(nativeCode)) + codeAddrs.Add(nativeCode); + + CalledMethod* pList = pCalledMethods; + + /* count called methods */ + while (pList != NULL) + { + TADDR callAddr = (TADDR)pList->GetCallAddr(); + if (!tmpCodeAddrs.Contains(callAddr) && !codeAddrs.Contains(callAddr)) { + tmpCodeAddrs.Add(callAddr); + } + pList = pList->GetNext(); + } + + SymbolCount = 1 + method.GetCount() + tmpCodeAddrs.GetCount(); + SymbolNames = new (nothrow) Elf_Symbol[SymbolCount]; + + pList = pCalledMethods; + int i = 1 + method.GetCount(); + while (i < SymbolCount && pList != NULL) + { + TADDR callAddr = (TADDR)pList->GetCallAddr(); + if (!codeAddrs.Contains(callAddr)) + { + MethodDesc* pMD = pList->GetMethodDesc(); + LPCUTF8 methodName = pMD->GetName(); + int symbolNameLength = strlen(methodName) + sizeof("__thunk_"); + SymbolNames[i].m_name = new char[symbolNameLength]; + SymbolNames[i].m_releaseName = true; + sprintf_s((char*)SymbolNames[i].m_name, symbolNameLength, "__thunk_%s", methodName); + SymbolNames[i].m_value = callAddr; + ++i; + codeAddrs.Add(callAddr); + } + CalledMethod* ptr = pList; + pList = pList->GetNext(); + delete ptr; + } + SymbolCount = i; + 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; + for (int i = 0; i < SymbolCount; ++i) + len += strlen(SymbolNames[i].m_name) + 1; len++; // end table with zero-length string buf.MemSize = len; @@ -921,10 +2445,11 @@ bool NotifyGdb::BuildStringTableSection(MemBuf& buf) if (buf.MemPtr == nullptr) return false; char* ptr = buf.MemPtr; - for (int i = 0; i < sizeof(SymbolNames) / sizeof(SymbolNames[0]); ++i) + for (int i = 0; i < SymbolCount; ++i) { - strcpy(ptr, SymbolNames[i]); - ptr += strlen(SymbolNames[i]) + 1; + SymbolNames[i].m_off = ptr - buf.MemPtr; + strcpy(ptr, SymbolNames[i].m_name); + ptr += strlen(SymbolNames[i].m_name) + 1; } buf.MemPtr[buf.MemSize-1] = 0; @@ -934,107 +2459,136 @@ bool NotifyGdb::BuildStringTableSection(MemBuf& buf) /* Build ELF .symtab section */ bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize) { - buf.MemSize = 2 * sizeof(Elf_Sym); + static const int textSectionIndex = GetSectionIndex(".text"); + + buf.MemSize = SymbolCount * 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; + sym[0].st_name = 0; + sym[0].st_info = 0; + sym[0].st_other = 0; + sym[0].st_value = 0; + sym[0].st_size = 0; + sym[0].st_shndx = SHN_UNDEF; - /* calculate total size */ - for (int i = 0; i < SectionNamesCount; ++i) + for (int i = 1; i < 1 + method.GetCount(); ++i) { - totalLength += strlen(SectionNames[i]) + 1; + sym[i].st_name = SymbolNames[i].m_off; + sym[i].setBindingAndType(STB_GLOBAL, STT_FUNC); + sym[i].st_other = 0; + sym[i].st_value = PINSTRToPCODE(SymbolNames[i].m_value - addr); + sym[i].st_shndx = textSectionIndex; + sym[i].st_size = SymbolNames[i].m_size; } - 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) + for (int i = 1 + method.GetCount(); i < SymbolCount; ++i) { - strcpy(bufPtr, SectionNames[i]); - bufPtr += strlen(SectionNames[i]) + 1; + sym[i].st_name = SymbolNames[i].m_off; + sym[i].setBindingAndType(STB_GLOBAL, STT_FUNC); + sym[i].st_other = 0; + sym[i].st_shndx = SectionNamesCount + (i - (1 + method.GetCount())); // .thunks section index + sym[i].st_size = 8; +#ifdef _TARGET_ARM_ + sym[i].st_value = 1; // for THUMB code +#else + sym[i].st_value = 0; +#endif } - return true; } -/* Build the ELF section headers table */ -bool NotifyGdb::BuildSectionTable(MemBuf& buf) +int NotifyGdb::GetSectionIndex(const char *sectName) { - Elf_Shdr* sectionHeaders = new (nothrow) Elf_Shdr[SectionNamesCount - 1]; - Elf_Shdr* pSh = sectionHeaders; + for (int i = 0; i < SectionNamesCount; ++i) + if (strcmp(SectionNames[i], sectName) == 0) + return i; + return -1; +} + +/* Build the ELF section headers table and section names table */ +bool NotifyGdb::BuildSectionTables(MemBuf& sectBuf, MemBuf& strBuf) +{ + static const int symtabSectionIndex = GetSectionIndex(".symtab"); + static const int nullSectionIndex = GetSectionIndex(""); + + const int thunks_count = SymbolCount - 1 - method.GetCount(); + // Approximate length of single section name. + // Used only to reduce memory reallocations. + static const int SECT_NAME_LENGTH = 11; + + if (!strBuf.Resize(SECT_NAME_LENGTH * (SectionNamesCount + thunks_count))) + { + return false; + } + + Elf_Shdr* sectionHeaders = new (nothrow) Elf_Shdr[SectionNamesCount + thunks_count]; 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) + + sectBuf.MemPtr = reinterpret_cast<char*>(sectionHeaders); + sectBuf.MemSize = sizeof(Elf_Shdr) * (SectionNamesCount + thunks_count); + + Elf_Shdr* pSh = sectionHeaders; + uint32_t sectNameOffset = 0; + + // Additional memory for remaining section names, + // grows twice on each reallocation. + int addSize = SECT_NAME_LENGTH; + + // Fill section headers and names + for (int i = 0; i < SectionNamesCount + thunks_count; ++i, ++pSh) { + char thunkSectNameBuf[256]; // temporary buffer for .thunk_# section name + const char *sectName; + + bool isThunkSection = i >= SectionNamesCount; + if (isThunkSection) + { + sprintf_s(thunkSectNameBuf, _countof(thunkSectNameBuf), ".thunk_%i", i); + sectName = thunkSectNameBuf; + } + else + { + sectName = SectionNames[i]; + } + + // Ensure that there is enough memory for section name, + // reallocate if necessary. pSh->sh_name = sectNameOffset; - sectNameOffset += strlen(SectionNames[i]) + 1; - pSh->sh_type = Sections[i].m_type; - pSh->sh_flags = Sections[i].m_flags; + sectNameOffset += strlen(sectName) + 1; + if (sectNameOffset > strBuf.MemSize) + { + // Allocate more memory for remaining section names + if (!strBuf.Resize(sectNameOffset + addSize)) + return false; + addSize *= 2; + } + + strcpy(strBuf.MemPtr + pSh->sh_name, sectName); + + // All .thunk_* sections have the same type and flags + int index = isThunkSection ? SectionNamesCount : i; + pSh->sh_type = Sections[index].m_type; + pSh->sh_flags = Sections[index].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; + pSh->sh_addralign = i == nullSectionIndex ? 0 : 1; + pSh->sh_entsize = i == symtabSectionIndex ? sizeof(Elf_Sym) : 0; } - buf.MemPtr = reinterpret_cast<char*>(sectionHeaders); - buf.MemSize = sizeof(Elf_Shdr) * (SectionNamesCount - 1); + // Set actual used size to avoid garbage in ELF section + strBuf.MemSize = sectNameOffset; return true; } @@ -1042,17 +2596,18 @@ bool NotifyGdb::BuildSectionTable(MemBuf& buf) 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; - + } + + buf.MemPtr = reinterpret_cast<char*>(header); + buf.MemSize = sizeof(Elf_Ehdr); return true; - } -/* Split full path name into directory & file anmes */ +/* Split full path name into directory & file names */ void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const char*& fileName) { char* pSlash = strrchr(path, '/'); @@ -1070,47 +2625,6 @@ void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const cha } } -/* 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) { |