/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: dwread.c Abstract: This module implements support for reading DWARF structures. Author: Evan Green 4-Dec-2015 Environment: Debug --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include "dwarfp.h" #include #include #include #include #include // // --------------------------------------------------------------------- Macros // // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // INT DwarfpIndexAbbreviations ( PDWARF_CONTEXT Context, ULONGLONG Offset, PUCHAR **AbbreviationsIndex, PUINTN IndexSize, PUINTN MaxAttributes ); INT DwarfpReadDie ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit, PUCHAR *Data, PUCHAR Abbreviation, PDWARF_DIE Die ); INT DwarfpReadFormValue ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit, PUCHAR *Data, PDWARF_ATTRIBUTE_VALUE Attribute ); VOID DwarfpPrintFormValue ( PDWARF_ATTRIBUTE_VALUE Attribute ); PSTR DwarfpGetTagName ( DWARF_TAG Tag ); PSTR DwarfpGetAttributeName ( DWARF_ATTRIBUTE Attribute ); PSTR DwarfpGetFormName ( DWARF_FORM Form ); PSTR DwarfpGetHasChildrenName ( DWARF_CHILDREN_VALUE Value ); // // -------------------------------------------------------------------- Globals // PSTR DwarfTagNames[] = { "NullTag", "DwarfTagArrayType", "DwarfTagClassType", "DwarfTagEntryPoint", "DwarfTagEnumerationType", "DwarfTagFormalParameter", NULL, NULL, "DwarfTagImportedDeclaration", NULL, "DwarfTagLabel", "DwarfTagLexicalBlock", NULL, "DwarfTagMember", NULL, "DwarfTagPointerType", "DwarfTagReferenceType", "DwarfTagCompileUnit", "DwarfTagStringType", "DwarfTagStructureType", NULL, "DwarfTagSubroutineType", "DwarfTagTypedef", "DwarfTagUnionType", "DwarfTagUnspecifiedParameters", "DwarfTagVariant", "DwarfTagCommonBlock", "DwarfTagCommonInclusion", "DwarfTagInheritance", "DwarfTagInlinedSubroutine", "DwarfTagModule", "DwarfTagPointerToMemberType", "DwarfTagSetType", "DwarfTagSubrangeType", "DwarfTagWithStatement", "DwarfTagAccessDeclaration", "DwarfTagBaseType", "DwarfTagCatchBlock", "DwarfTagConstType", "DwarfTagConstant", "DwarfTagEnumerator", "DwarfTagFileType", "DwarfTagFriend", "DwarfTagNameList", "DwarfTagNameListItem", "DwarfTagPackedType", "DwarfTagSubprogram", "DwarfTagTemplateTypeParameter", "DwarfTagTemplateValueParameter", "DwarfTagThrownType", "DwarfTagTryBlock", "DwarfTagVariantPart", "DwarfTagVariable", "DwarfTagVolatileType", "DwarfTagDwarfProcedure", "DwarfTagRestrictType", "DwarfTagInterfaceType", "DwarfTagNamespace", "DwarfTagImportedModule", "DwarfTagUnspecifiedType", "DwarfTagPartialUnit", "DwarfTagImportedUnit", NULL, "DwarfTagCondition", "DwarfTagSharedType", "DwarfTagTypeUnit", "DwarfTagRvalueReferenceType", "DwarfTagTemplateAlias", }; PSTR DwarfHasChildrenNames[] = { "NoChildren", "HasChildren", }; PSTR DwarfAttributeNames[] = { "DwarfAtNull", "DwarfAtSibling", "DwarfAtLocation", "DwarfAtName", NULL, NULL, NULL, NULL, NULL, "DwarfAtOrdering", NULL, "DwarfAtByteSize", "DwarfAtBitOffset", "DwarfAtBitSize", NULL, NULL, "DwarfAtStatementList", "DwarfAtLowPc", "DwarfAtHighPc", "DwarfAtLanguage", NULL, "DwarfAtDiscr", "DwarfAtDiscrValue", "DwarfAtVisibility", "DwarfAtImport", "DwarfAtStringLength", "DwarfAtCommonReference", "DwarfAtCompDir", "DwarfAtConstValue", "DwarfAtContainingType", "DwarfAtDefaultValue", NULL, "DwarfAtInline", "DwarfAtIsOptional", "DwarfAtLowerBound", NULL, NULL, "DwarfAtProducer", NULL, "DwarfAtPrototyped", NULL, NULL, "DwarfAtReturnAddress", NULL, "DwarfAtStartScope", NULL, "DwarfAtBitStride", "DwarfAtUpperBound", NULL, "DwarfAtAbstractOrigin", "DwarfAtAccessibility", "DwarfAtAddressClass", "DwarfAtArtificial", "DwarfAtBaseTypes", "DwarfAtCallingConvention", "DwarfAtCount", "DwarfAtDataMemberLocation", "DwarfAtDeclColumn", "DwarfAtDeclFile", "DwarfAtDeclLine", "DwarfAtDeclaration", "DwarfAtDiscrList", "DwarfAtEncoding", "DwarfAtExternal", "DwarfAtFrameBase", "DwarfAtFriend", "DwarfAtIdentifierCase", "DwarfAtMacroInfo", "DwarfAtNameListItem", "DwarfAtPriority", "DwarfAtSegment", "DwarfAtSpecification", "DwarfAtStaticLink", "DwarfAtType", "DwarfAtUseLocation", "DwarfAtVariableParameter", "DwarfAtVirtuality", "DwarfAtVtableElementLocation", "DwarfAtAllocated", "DwarfAtAssociated", "DwarfAtDataLocation", "DwarfAtByteStride", "DwarfAtEntryPc", "DwarfAtUseUtf8", "DwarfAtExtension", "DwarfAtRanges", "DwarfAtTrampoline", "DwarfAtCallColumn", "DwarfAtCallFile", "DwarfAtCallLine", "DwarfAtDescription", "DwarfAtBinaryScale", "DwarfAtDecimalScale", "DwarfAtSmall", "DwarfAtDecimalSign", "DwarfAtDigitCount", "DwarfAtPictureString", "DwarfAtMutable", "DwarfAtThreadsScaled", "DwarfAtExplicit", "DwarfAtObjectPointer", "DwarfAtEndianity", "DwarfAtElemental", "DwarfAtPure", "DwarfAtRecursive", "DwarfAtSignature", "DwarfAtMainSubprogram", "DwarfAtDataBitOffset", "DwarfAtConstExpression", "DwarfAtEnumClass", "DwarfAtLinkageName", }; PSTR DwarfFormNames[] = { "DwarfFormNull", "DwarfFormAddress", NULL, "DwarfFormBlock2", "DwarfFormBlock4", "DwarfFormData2", "DwarfFormData4", "DwarfFormData8", "DwarfFormString", "DwarfFormBlock", "DwarfFormBlock1", "DwarfFormData1", "DwarfFormFlag", "DwarfFormSData", "DwarfFormStringPointer", "DwarfFormUData", "DwarfFormRefAddress", "DwarfFormRef1", "DwarfFormRef2", "DwarfFormRef4", "DwarfFormRef8", "DwarfFormRefUData", "DwarfFormIndirect", "DwarfFormSecOffset", "DwarfFormExprLoc", "DwarfFormFlagPresent", NULL, NULL, NULL, NULL, NULL, NULL, "DwarfFormRefSig8" }; // // ------------------------------------------------------------------ Functions // VOID DwarfpReadCompilationUnit ( PUCHAR *Data, PULONGLONG Size, PDWARF_COMPILATION_UNIT Unit ) /*++ Routine Description: This routine reads a DWARF compilation unit header, and pieces it out into a structure. Arguments: Data - Supplies a pointer that on input contains a pointer to the header. On output this pointer will be advanced past the header and the DIEs, meaning it will point at the next compilation unit. Size - Supplies a pointer that on input contains the size of the section. On output this will be decreased by the amount that the data was advanced. Unit - Supplies a pointer where the header information will be filled in. Return Value: None. --*/ { ULONGLONG Delta; ULONGLONG RemainingSize; Unit->Start = *Data; RemainingSize = *Size; DwarfpReadInitialLength(Data, &(Unit->Is64Bit), &(Unit->UnitLength)); Unit->Version = DwarfpRead2(Data); Unit->AbbreviationOffset = DWARF_READN(Data, Unit->Is64Bit); Unit->AddressSize = DwarfpRead1(Data); Unit->Dies = *Data; // // Advance past the DIEs themselves as well. // *Data = Unit->Start + Unit->UnitLength + sizeof(ULONG); if (Unit->Is64Bit != FALSE) { *Data += sizeof(ULONGLONG); } Unit->DiesEnd = *Data; Delta = *Data - Unit->Start; assert(Delta <= RemainingSize); RemainingSize -= Delta; *Size = RemainingSize; return; } INT DwarfpLoadCompilationUnit ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit ) /*++ Routine Description: This routine processes all the DIEs within a DWARF compilation unit. Arguments: Context - Supplies a pointer to the application context. Unit - Supplies a pointer to the compilation unit. Return Value: 0 on success. Returns an error number on failure. --*/ { PUCHAR Abbreviation; DWARF_LEB128 AbbreviationNumber; PUCHAR *Abbreviations; UINTN AbbreviationsCount; UINTN AllocationSize; ULONG Depth; PDWARF_DIE Die; PUCHAR DieBytes; PUCHAR End; UINTN MaxAttributes; PDWARF_DIE Parent; INT Status; Abbreviations = NULL; Depth = 0; Die = NULL; End = Unit->DiesEnd; Parent = NULL; // // Index the abbreviations for this compilation unit. // Status = DwarfpIndexAbbreviations(Context, Unit->AbbreviationOffset, &Abbreviations, &AbbreviationsCount, &MaxAttributes); if (Status != 0) { goto ProcessCompilationUnitEnd; } AllocationSize = sizeof(DWARF_DIE) + (MaxAttributes * sizeof(DWARF_ATTRIBUTE_VALUE)); // // Loop through all the DIEs. // DieBytes = Unit->Dies; while (DieBytes < End) { Die = malloc(AllocationSize); if (Die == NULL) { Status = errno; goto ProcessCompilationUnitEnd; } memset(Die, 0, AllocationSize); INITIALIZE_LIST_HEAD(&(Die->ChildList)); Die->Capacity = MaxAttributes; Die->Attributes = (PDWARF_ATTRIBUTE_VALUE)(Die + 1); Die->Start = DieBytes; Die->Depth = Depth; AbbreviationNumber = DwarfpReadLeb128(&DieBytes); Die->AbbreviationNumber = AbbreviationNumber; Die->Parent = Parent; // // Skip null entries. // if (AbbreviationNumber == 0) { if ((Context->Flags & DWARF_CONTEXT_DEBUG) != 0) { DWARF_PRINT(" <%x> Null Entry\n", Die->Start - (PUCHAR)(Context->Sections.Info.Data)); } if (Depth != 0) { Depth -= 1; Parent = Parent->Parent; } continue; } if ((AbbreviationNumber >= AbbreviationsCount) || (Abbreviations[AbbreviationNumber] == NULL)) { DWARF_ERROR("DWARF: Bad abbreviation number %I64d\n", AbbreviationNumber); Status = EINVAL; goto ProcessCompilationUnitEnd; } Abbreviation = Abbreviations[AbbreviationNumber]; Status = DwarfpReadDie(Context, Unit, &DieBytes, Abbreviation, Die); if (Status != 0) { DWARF_ERROR("DWARF: Invalid DIE.\n"); goto ProcessCompilationUnitEnd; } if (Parent != NULL) { INSERT_BEFORE(&(Die->ListEntry), &(Parent->ChildList)); } else { INSERT_BEFORE(&(Die->ListEntry), &(Unit->DieList)); } if ((Die->Flags & DWARF_DIE_HAS_CHILDREN) != 0) { Depth += 1; Parent = Die; } } Die = NULL; Status = 0; ProcessCompilationUnitEnd: if (Abbreviations != NULL) { free(Abbreviations); } if (Die != NULL) { DwarfpDestroyDie(Context, Die); } return Status; } VOID DwarfpDestroyCompilationUnit ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit ) /*++ Routine Description: This routine destroys a compilation unit. It's assumed it's already off the list. Arguments: Context - Supplies a pointer to the application context. Unit - Supplies a pointer to the compilation unit to destroy. Return Value: None. --*/ { PDWARF_DIE Die; assert(Unit->ListEntry.Next == NULL); while (!LIST_EMPTY(&(Unit->DieList))) { Die = LIST_VALUE(Unit->DieList.Next, DWARF_DIE, ListEntry); LIST_REMOVE(&(Die->ListEntry)); Die->ListEntry.Next = NULL; DwarfpDestroyDie(Context, Die); } free(Unit); return; } VOID DwarfpDestroyDie ( PDWARF_CONTEXT Context, PDWARF_DIE Die ) /*++ Routine Description: This routine destroys a Debug Information Entry. Arguments: Context - Supplies a pointer to the application context. Die - Supplies a pointer to the DIE to destroy. Return Value: None. --*/ { PDWARF_DIE Child; assert(Die->ListEntry.Next == NULL); while (!LIST_EMPTY(&(Die->ChildList))) { Child = LIST_VALUE(Die->ChildList.Next, DWARF_DIE, ListEntry); LIST_REMOVE(&(Child->ListEntry)); Child->ListEntry.Next = NULL; DwarfpDestroyDie(Context, Child); } free(Die); return; } PSTR DwarfpGetStringAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute ) /*++ Routine Description: This routine returns the given attribute with type string. Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Return Value: Returns a pointer to the string on success. NULL if no such attribute exists, or its type is not a string. --*/ { PDWARF_ATTRIBUTE_VALUE Value; Value = DwarfpGetAttribute(Die, Attribute); if ((Value != NULL) && ((Value->Form == DwarfFormString) || (Value->Form == DwarfFormStringPointer))) { return Value->Value.String; } return NULL; } BOOL DwarfpGetAddressAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute, PULONGLONG Address ) /*++ Routine Description: This routine returns the given attribute, ensuring it is of type address. Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Address - Supplies a pointer where the address is returned on success. Return Value: TRUE if an address was retrieved. FALSE if no address was retrieved or it was not of type address. --*/ { PDWARF_ATTRIBUTE_VALUE Value; Value = DwarfpGetAttribute(Die, Attribute); if ((Value != NULL) && (Value->Form == DwarfFormAddress)) { *Address = Value->Value.Address; return TRUE; } return FALSE; } BOOL DwarfpGetIntegerAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute, PULONGLONG Integer ) /*++ Routine Description: This routine returns the given attribute, ensuring it is of type integer (data or flag). Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Integer - Supplies a pointer where the integer is returned on success. Return Value: TRUE if an address was retrieved. FALSE if no address was retrieved or it was not of type address. --*/ { BOOL Result; PDWARF_ATTRIBUTE_VALUE Value; Result = FALSE; Value = DwarfpGetAttribute(Die, Attribute); if (Value != NULL) { Result = TRUE; switch (Value->Form) { case DwarfFormData1: case DwarfFormData2: case DwarfFormData4: case DwarfFormData8: case DwarfFormSData: case DwarfFormUData: *Integer = Value->Value.UnsignedConstant; break; case DwarfFormFlag: case DwarfFormFlagPresent: *Integer = Value->Value.Flag; break; default: Result = FALSE; break; } } return Result; } BOOL DwarfpGetTypeReferenceAttribute ( PDWARF_CONTEXT Context, PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute, PSOURCE_FILE_SYMBOL *File, PLONG Identifier ) /*++ Routine Description: This routine reads a given attribute and converts that reference into a symbol type reference tuple. Arguments: Context - Supplies a pointer to the parsing context. Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. File - Supplies a pointer where a pointer to the file will be returned on success. Identifier - Supplies a pointer where the type identifier will be returned on success. Return Value: TRUE if a value was retrieved. FALSE if no value was retrieved or it was not of type reference. --*/ { PDWARF_LOADING_CONTEXT LoadingContext; BOOL Result; ULONGLONG TypeOffset; LoadingContext = Context->LoadingContext; assert(LoadingContext != NULL); Result = DwarfpGetLocalReferenceAttribute(Die, Attribute, &TypeOffset); if (Result != FALSE) { assert(LoadingContext->CurrentFile != NULL); *File = LoadingContext->CurrentFile; // // Make the local reference, which is an offset from the start of the // compilation unit header, global to the entire debug info section. // TypeOffset += (PVOID)(LoadingContext->CurrentUnit->Start) - Context->Sections.Info.Data; *Identifier = TypeOffset; } else { Result = DwarfpGetGlobalReferenceAttribute(Die, DwarfAtType, &TypeOffset); if (Result == FALSE) { // // Void types don't have a type attribute. // *File = NULL; *Identifier = -1; return TRUE; } // // Consider supporting this type that is global to the whole .debug_info // section. The tricky bit is figuring out which source file // (compilation unit) the offset belongs to. // assert(FALSE); return FALSE; } return TRUE; } BOOL DwarfpGetLocalReferenceAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute, PULONGLONG Offset ) /*++ Routine Description: This routine returns the given attribute, ensuring it is of type reference. Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Offset - Supplies a pointer where the DIE offset is returned on success. Return Value: TRUE if a value was retrieved. FALSE if no value was retrieved or it was not of type reference. --*/ { BOOL Result; PDWARF_ATTRIBUTE_VALUE Value; Result = FALSE; Value = DwarfpGetAttribute(Die, Attribute); if (Value != NULL) { Result = TRUE; switch (Value->Form) { case DwarfFormRef1: case DwarfFormRef2: case DwarfFormRef4: case DwarfFormRef8: case DwarfFormRefUData: case DwarfFormData1: case DwarfFormData2: case DwarfFormData4: case DwarfFormData8: case DwarfFormUData: *Offset = Value->Value.Offset; break; default: Result = FALSE; break; } } return Result; } BOOL DwarfpGetGlobalReferenceAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute, PULONGLONG Offset ) /*++ Routine Description: This routine returns the given attribute, ensuring it is of type reference address. Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Offset - Supplies a pointer where the DIE offset is returned on success. Return Value: TRUE if a value was retrieved. FALSE if no value was retrieved or it was not of type reference. --*/ { PDWARF_ATTRIBUTE_VALUE Value; Value = DwarfpGetAttribute(Die, Attribute); if ((Value != NULL) && (Value->Form == DwarfFormRefAddress)) { *Offset = Value->Value.Offset; return TRUE; } return FALSE; } PDWARF_ATTRIBUTE_VALUE DwarfpGetAttribute ( PDWARF_DIE Die, DWARF_ATTRIBUTE Attribute ) /*++ Routine Description: This routine returns the requested attribute from a DIE. Arguments: Die - Supplies a pointer to the DIE to get the attribute from. Attribute - Supplies the attribute to retrieve. Return Value: Returns a pointer to the attribute value on success. NULL if no such attribute exists. --*/ { UINTN Index; for (Index = 0; Index < Die->Count; Index += 1) { if (Die->Attributes[Index].Name == Attribute) { return &(Die->Attributes[Index]); } } return NULL; } INT DwarfpSearchLocationList ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit, UINTN Offset, ULONGLONG Pc, PUCHAR *LocationExpression, PUINTN LocationExpressionSize ) /*++ Routine Description: This routine searches a location list and returns the expression that matches the given PC value. Arguments: Context - Supplies a pointer to the DWARF symbol context. Unit - Supplies a pointer to the current compilation unit. Offset - Supplies the byte offset into the location list section of the list to search. Pc - Supplies the current PC value to match against. LocationExpression - Supplies a pointer where a pointer to the location expression that matched will be returned. LocationExpressionSize - Supplies a pointer where the size of the location expression will be returned on success. Return Value: 0 if an expression matched. EAGAIN if the locations section is missing. ENOENT if none of the entries matched the current PC value. --*/ { ULONGLONG Base; PUCHAR Bytes; ULONGLONG End; USHORT Length; ULONGLONG Start; Base = Unit->LowPc; if (Context->Sections.Locations.Size == 0) { DWARF_ERROR("DWARF: Missing .debug_loc section.\n"); return EAGAIN; } assert(Context->Sections.Locations.Size > Offset); Bytes = Context->Sections.Locations.Data + Offset; while (TRUE) { if (Unit->AddressSize == 8) { Start = DwarfpRead8(&Bytes); End = DwarfpRead8(&Bytes); // // If the start is the max address, then it's a base address update. // if (Start == MAX_ULONGLONG) { Base = End; continue; } } else { assert(Unit->AddressSize == 4); Start = DwarfpRead4(&Bytes); End = DwarfpRead4(&Bytes); if (Start == MAX_ULONG) { Base = End; continue; } } // // If the start and end are both zero, this is a termination entry. // if ((Start == 0) && (End == 0)) { break; } Length = DwarfpRead2(&Bytes); // // If the PC fits in these bounds, then return the entry contents. // if ((Pc >= Start + Base) && (Pc < End + Base)) { *LocationExpression = Bytes; *LocationExpressionSize = Length; return 0; } Bytes += Length; } return ENOENT; } DWARF_LEB128 DwarfpReadLeb128 ( PUCHAR *Data ) /*++ Routine Description: This routine reads a DWARF unsigned LEB128 variable length encoded value. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { UCHAR Byte; DWARF_LEB128 Result; ULONG Shift; // // LEB128 numbers encode 7 bits in each byte, with the upper bit signifying // whether there are more bytes (1) or this is the last byte (0). // Result = 0; Shift = 0; while (TRUE) { Byte = DwarfpRead1(Data); Result |= ((ULONGLONG)(Byte & 0x7F)) << Shift; Shift += 7; if ((Byte & 0x80) == 0) { break; } } // // Anything larger than a 64-bit integer would overflow the current // data representation. // assert(Shift <= 64); return Result; } DWARF_SLEB128 DwarfpReadSleb128 ( PUCHAR *Data ) /*++ Routine Description: This routine reads a DWARF signed LEB128 variable length encoded value. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { UCHAR Byte; DWARF_SLEB128 Result; ULONG Shift; // // LEB128 numbers encode 7 bits in each byte, with the upper bit signifying // whether there are more bytes (1) or this is the last byte (0). // Result = 0; Shift = 0; while (TRUE) { Byte = DwarfpRead1(Data); Result |= ((ULONGLONG)(Byte & 0x7F)) << Shift; Shift += 7; if ((Byte & 0x80) == 0) { break; } } // // If the high order bit of the last byte is set, sign extend to the // remainder of the value. // if ((Shift < (sizeof(DWARF_SLEB128) * BITS_PER_BYTE)) && ((Byte & 0x40) != 0)) { Result |= -(1LL << Shift); } // // Anything larger than a 64-bit integer would overflow the current // data representation. // assert(Shift <= 64); return Result; } VOID DwarfpReadInitialLength ( PUCHAR *Data, PBOOL Is64Bit, PULONGLONG Value ) /*++ Routine Description: This routine reads an initial length member from a DWARF header. The initial length is either 32-bits for 32-bit sections, or 96-bits for 64-bit sections. Arguments: Data - Supplies a pointer that on input contains a pointer to the header. On output this pointer will be advanced past the initial length. Is64Bit - Supplies a pointer where a boolean will be returned indicating whether or not this is a 64-bit section. Most sections, even in 64-bit code, are not. Value - Supplies a pointer where the actual initial length value will be returned. Return Value: None. --*/ { *Is64Bit = FALSE; *Value = DwarfpRead4(Data); if (*Value == 0xFFFFFFFF) { *Is64Bit = TRUE; *Value = DwarfpRead8(Data); } return; } UCHAR DwarfpRead1 ( PUCHAR *Data ) /*++ Routine Description: This routine reads a byte from the DWARF data stream and advances the stream. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { UCHAR Value; Value = *(*Data); *Data += sizeof(UCHAR); return Value; } USHORT DwarfpRead2 ( PUCHAR *Data ) /*++ Routine Description: This routine reads two bytes from the DWARF data stream and advances the stream. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { USHORT Value; Value = *((PUSHORT)*Data); *Data += sizeof(USHORT); return Value; } ULONG DwarfpRead4 ( PUCHAR *Data ) /*++ Routine Description: This routine reads four bytes from the DWARF data stream and advances the stream. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { ULONG Value; Value = *((PULONG)*Data); *Data += sizeof(ULONG); return Value; } ULONGLONG DwarfpRead8 ( PUCHAR *Data ) /*++ Routine Description: This routine reads eight bytes from the DWARF data stream and advances the stream. Arguments: Data - Supplies a pointer that on input contains a pointer to the data. On output this pointer will be advanced past the value read. Return Value: Returns the read value. --*/ { ULONGLONG Value; Value = *((PULONGLONG)*Data); *Data += sizeof(ULONGLONG); return Value; } // // --------------------------------------------------------- Internal Functions // INT DwarfpIndexAbbreviations ( PDWARF_CONTEXT Context, ULONGLONG Offset, PUCHAR **AbbreviationsIndex, PUINTN IndexSize, PUINTN MaxAttributes ) /*++ Routine Description: This routine creates an array of pointers to abbreviation numbers for the abbreviations in a compilation unit. The index makes abbreviation lookup instant instead of O(N). Arguments: Context - Supplies a pointer to tha parsing context. Offset - Supplies the offset into the abbreviation section where abbreviations for this compilation unit begin. AbbreviationsIndex - Supplies a pointer where an array of pointers will be returned, indexed by abbreviation number. The caller is responsible for freeing this memory. IndexSize - Supplies a pointer where the number of elements in the array will be returned. MaxAttributes - Supplies a pointer where the maximum number of attributes in any DIE template found will be returned. Return Value: 0 on success. ENOMEM on failure. --*/ { PUCHAR *Array; PUCHAR Bytes; UINTN Count; DWARF_LEB128 Form; UCHAR HasChildren; UINTN MaxIndex; DWARF_LEB128 Name; PVOID NewArray; UINTN NewSize; DWARF_LEB128 Number; UINTN Size; DWARF_LEB128 Tag; *MaxAttributes = 0; Bytes = Context->Sections.Abbreviations.Data + Offset; if (Offset >= Context->Sections.Abbreviations.Size) { return ERANGE; } MaxIndex = 0; Size = 64; Array = malloc(Size * sizeof(PVOID)); if (Array == NULL) { return ENOMEM; } memset(Array, 0, Size * sizeof(PVOID)); if ((Context->Flags & DWARF_CONTEXT_DEBUG_ABBREVIATIONS) != 0) { DWARF_PRINT("Abbreviations at offset %I64x:\n", Offset); } // // Loop processing abbreviations. // while (TRUE) { Number = DwarfpReadLeb128(&Bytes); if (Number == 0) { break; } // // Reallocate the array if needed. // if (Number >= Size) { NewSize = Size * 2; while (NewSize <= Number) { NewSize *= 2; } NewArray = realloc(Array, NewSize * sizeof(PVOID)); if (NewArray == NULL) { free(Array); return ENOMEM; } memset(NewArray + (Size * sizeof(PVOID)), 0, (NewSize - Size) * sizeof(PVOID)); Size = NewSize; Array = NewArray; } // // Abbreviations should not collide. // assert(Array[Number] == NULL); Array[Number] = Bytes; if (Number > MaxIndex) { MaxIndex = Number; } // // Get past the tag and the children byte. // Tag = DwarfpReadLeb128(&Bytes); HasChildren = DwarfpRead1(&Bytes); if ((Context->Flags & DWARF_CONTEXT_DEBUG_ABBREVIATIONS) != 0) { DWARF_PRINT(" %I64d: %s %s\n", Number, DwarfpGetTagName(Tag), DwarfpGetHasChildrenName(HasChildren)); } // // Now get past the attributes specifications. // Count = 0; while (TRUE) { Name = DwarfpReadLeb128(&Bytes); Form = DwarfpReadLeb128(&Bytes); if ((Name == 0) && (Form == 0)) { break; } if ((Context->Flags & DWARF_CONTEXT_DEBUG_ABBREVIATIONS) != 0) { DWARF_PRINT(" %s %s\n", DwarfpGetAttributeName(Name), DwarfpGetFormName(Form)); } Count += 1; } if (Count > *MaxAttributes) { *MaxAttributes = Count; } } *AbbreviationsIndex = Array; *IndexSize = MaxIndex + 1; return 0; } INT DwarfpReadDie ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit, PUCHAR *Data, PUCHAR Abbreviation, PDWARF_DIE Die ) /*++ Routine Description: This routine reads a single Debug Information Unit using the abbreviation template. Arguments: Context - Supplies a pointer to the application context. Unit - Supplies a pointer to the compilation unit. Data - Supplies a pointer that on input contains a pointer to the values. On output this pointer will be advanced past the contents. Abbreviation - Supplies a pointer to the abbreviation, which marks out the form of the data. Die - Supplies a pointer where the DIE is returned on success. Return Value: 0 on success. Returns an error number on failure. --*/ { PDWARF_ATTRIBUTE_VALUE Attribute; DWARF_FORM Form; DWARF_CHILDREN_VALUE HasChildren; DWARF_ATTRIBUTE Name; INT Status; DWARF_LEB128 Tag; // // Get past the tag and the children byte. // Tag = DwarfpReadLeb128(&Abbreviation); Die->Tag = Tag; HasChildren = DwarfpRead1(&Abbreviation); if ((Context->Flags & DWARF_CONTEXT_DEBUG) != 0) { DWARF_PRINT(" <%x><%x> %s %s Abbrev. %I64d\n", Die->Depth, (PVOID)(Die->Start) - Context->Sections.Info.Data, DwarfpGetTagName(Tag), DwarfpGetHasChildrenName(HasChildren), Die->AbbreviationNumber); } if (HasChildren == DwarfChildrenYes) { Die->Flags |= DWARF_DIE_HAS_CHILDREN; } Attribute = &(Die->Attributes[Die->Count]); // // Read in each attribute value. // while (TRUE) { Name = DwarfpReadLeb128(&Abbreviation); Form = DwarfpReadLeb128(&Abbreviation); if ((Name == 0) && (Form == 0)) { break; } assert(Die->Count < Die->Capacity); Attribute->Name = Name; Attribute->Form = Form; if ((Context->Flags & DWARF_CONTEXT_DEBUG) != 0) { DWARF_PRINT(" <%x> %s : ", *Data - (PUCHAR)(Context->Sections.Info.Data), DwarfpGetAttributeName(Name)); } Status = DwarfpReadFormValue(Context, Unit, Data, Attribute); if (Status != 0) { DWARF_ERROR("DWARF: Failed to read attribute.\n"); continue; } if ((Context->Flags & DWARF_CONTEXT_DEBUG) != 0) { DwarfpPrintFormValue(Attribute); // // Print the expression if it's an expression or it's a block and // has a known name. // if ((Attribute->Form == DwarfFormExprLoc) || ((DWARF_BLOCK_FORM(Attribute->Form)) && ((Attribute->Name == DwarfAtDataLocation) || (Attribute->Name == DwarfAtDataMemberLocation)))) { DWARF_PRINT(" ("); DwarfpPrintExpression(Context, Unit->AddressSize, Unit, Attribute->Value.Block.Data, Attribute->Value.Block.Size); DWARF_PRINT(")"); } DWARF_PRINT("\n"); } Die->Count += 1; Attribute += 1; } return 0; } INT DwarfpReadFormValue ( PDWARF_CONTEXT Context, PDWARF_COMPILATION_UNIT Unit, PUCHAR *Data, PDWARF_ATTRIBUTE_VALUE Attribute ) /*++ Routine Description: This routine reads a DWARF attribute based on its form. Arguments: Context - Supplies a pointer to the application context. Unit - Supplies a pointer to the compilation unit. Data - Supplies a pointer that on input contains a pointer to the values. On output this pointer will be advanced past the contents. Attribute - Supplies a pointer to the attribute to read the value for. The form is expected to have already been filled in. Return Value: 0 on success. Returns an error number on failure. --*/ { INT Status; PDWARF_FORM_VALUE Value; Status = 0; Value = &(Attribute->Value); switch (Attribute->Form) { case DwarfFormAddress: if (Unit->AddressSize == 8) { Value->Address = DwarfpRead8(Data); } else { assert(Unit->AddressSize == 4); Value->Address = DwarfpRead4(Data); } break; // // This form is just a generic block of bytes, but its size can be defined // in a number of different ways. // case DwarfFormBlock1: case DwarfFormBlock2: case DwarfFormBlock4: case DwarfFormBlock: switch (Attribute->Form) { case DwarfFormBlock1: Value->Block.Size = DwarfpRead1(Data); break; case DwarfFormBlock2: Value->Block.Size = DwarfpRead2(Data); break; case DwarfFormBlock4: Value->Block.Size = DwarfpRead4(Data); break; case DwarfFormBlock: Value->Block.Size = DwarfpReadLeb128(Data); break; default: assert(FALSE); break; } Value->Block.Data = *Data; *Data += Value->Block.Size; break; // // Then there are the constants. // case DwarfFormData1: Value->UnsignedConstant = DwarfpRead1(Data); break; case DwarfFormData2: Value->UnsignedConstant = DwarfpRead2(Data); break; case DwarfFormData4: Value->UnsignedConstant = DwarfpRead4(Data); break; case DwarfFormData8: case DwarfFormRefSig8: Value->UnsignedConstant = DwarfpRead8(Data); break; case DwarfFormSData: Value->SignedConstant = DwarfpReadSleb128(Data); break; case DwarfFormUData: Value->UnsignedConstant = DwarfpReadLeb128(Data); break; // // The expression location form uses the same members as the block. It // represents a DWARF expression. // case DwarfFormExprLoc: Value->Block.Size = DwarfpReadLeb128(Data); Value->Block.Data = *Data; *Data += Value->Block.Size; break; // // Handle the flag forms. // case DwarfFormFlag: Value->Flag = DwarfpRead1(Data); break; case DwarfFormFlagPresent: Value->Flag = TRUE; break; // // The pointers to other sections all look the same from a data perspective. // case DwarfFormSecOffset: case DwarfFormRefAddress: Value->Offset = DWARF_READN(Data, Unit->Is64Bit); break; // // Handle references to other DIEs within this compilation unit. // case DwarfFormRef1: Value->Offset = DwarfpRead1(Data); break; case DwarfFormRef2: Value->Offset = DwarfpRead2(Data); break; case DwarfFormRef4: Value->Offset = DwarfpRead4(Data); break; case DwarfFormRef8: Value->Offset = DwarfpReadLeb128(Data); break; case DwarfFormRefUData: Value->Offset = DwarfpReadLeb128(Data); break; case DwarfFormString: Value->String = (PSTR)*Data; *Data += strlen((PSTR)*Data) + 1; break; case DwarfFormStringPointer: Value->Offset = DWARF_READN(Data, Unit->Is64Bit); assert(Value->Offset < Context->Sections.Strings.Size); Value->String = Context->Sections.Strings.Data + Value->Offset; break; default: DWARF_ERROR("DWARF: Unknown form %d.\n", Attribute->Form); Status = EINVAL; break; } return Status; } VOID DwarfpPrintFormValue ( PDWARF_ATTRIBUTE_VALUE Attribute ) /*++ Routine Description: This routine prints a form value. Arguments: Attribute - Supplies a pointer to the attribute, whose form and value should already be filled in. Return Value: None. --*/ { PUCHAR Bytes; UINTN Index; PDWARF_FORM_VALUE Value; Value = &(Attribute->Value); switch (Attribute->Form) { case DwarfFormAddress: DWARF_PRINT("%I64x", Value->Address); break; // // This form is just a generic block of bytes, but its size can be defined // in a number of different ways. // case DwarfFormBlock1: case DwarfFormBlock2: case DwarfFormBlock4: case DwarfFormBlock: DWARF_PRINT("%I64u byte block: ", Value->Block.Size); Bytes = Value->Block.Data; for (Index = 0; Index < Value->Block.Size; Index += 1) { DWARF_PRINT("%02x ", Bytes[Index]); } break; // // Then there are the constants. // case DwarfFormData1: case DwarfFormData2: case DwarfFormData4: case DwarfFormData8: case DwarfFormUData: DWARF_PRINT("%I64u", Value->UnsignedConstant); break; case DwarfFormRefSig8: DWARF_PRINT("TypeSig %I64x", Value->TypeSignature); break; case DwarfFormSData: DWARF_PRINT("%+I64d", Value->SignedConstant); break; // // The expression location form uses the same members as the block. It // represents a DWARF expression. // case DwarfFormExprLoc: DWARF_PRINT("%I64u byte expression: ", Value->Block.Size); Bytes = Value->Block.Data; for (Index = 0; Index < Value->Block.Size; Index += 1) { DWARF_PRINT("%02x ", Bytes[Index]); } break; // // Handle the flag forms. // case DwarfFormFlag: case DwarfFormFlagPresent: DWARF_PRINT("%d", Value->Flag); break; // // The pointers to other sections all look the same from a data perspective. // case DwarfFormSecOffset: DWARF_PRINT("SectionOffset %I64x", Value->Offset); break; case DwarfFormRefAddress: DWARF_PRINT("RefAddress %I64x", Value->Offset); break; case DwarfFormRef1: case DwarfFormRef2: case DwarfFormRef4: case DwarfFormRef8: case DwarfFormRefUData: DWARF_PRINT("<%I64x>", Value->Offset); break; case DwarfFormString: case DwarfFormStringPointer: DWARF_PRINT("\"%s\"", Value->String); break; default: DWARF_ERROR("DWARF: Unknown form %d.\n", Attribute->Form); break; } return; } PSTR DwarfpGetTagName ( DWARF_TAG Tag ) /*++ Routine Description: This routine returns the string description of a given dwarf tag. Arguments: Tag - Supplies the tag to convert. Return Value: Returns the tag name string. --*/ { PSTR Name; Name = NULL; if (Tag <= DwarfTagTemplateAlias) { Name = DwarfTagNames[Tag]; } else if ((Tag >= DwarfTagLowUser) && (Tag <= DwarfTagHighUser)) { Name = "DwarfTagUser"; } if (Name == NULL) { Name = "DwarfTagUNKNOWN"; } return Name; } PSTR DwarfpGetAttributeName ( DWARF_ATTRIBUTE Attribute ) /*++ Routine Description: This routine returns the string description of a given dwarf attribute. Arguments: Attribute - Supplies the attribute to convert. Return Value: Returns the attribute name string. --*/ { PSTR Name; Name = NULL; if (Attribute <= DwarfAtLinkageName) { Name = DwarfAttributeNames[Attribute]; } else if ((Attribute >= DwarfAtLowUser) && (Attribute <= DwarfAtHighUser)) { Name = "DwarfAtUser"; } if (Name == NULL) { Name = "DwarfAtUNKNOWN"; } return Name; } PSTR DwarfpGetFormName ( DWARF_FORM Form ) /*++ Routine Description: This routine returns the string description of a given dwarf form. Arguments: Form - Supplies the form to convert. Return Value: Returns the form name string. --*/ { PSTR Name; Name = NULL; if (Form <= DwarfFormRefSig8) { Name = DwarfFormNames[Form]; } if (Name == NULL) { Name = "DwarfFormUNKNOWN"; } return Name; } PSTR DwarfpGetHasChildrenName ( DWARF_CHILDREN_VALUE Value ) /*++ Routine Description: This routine returns the string description of a given dwarf form. Arguments: Value - Supplies the value to convert. Return Value: Returns the has children name string. --*/ { if (Value <= DwarfChildrenYes) { return DwarfHasChildrenNames[Value]; } return "DwarfChildrenINVALID"; }