/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: dwline.c Abstract: This module implements support for processing the DWARF 2+ line number program. Author: Evan Green 8-Dec-2015 Environment: Debug --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include "dwarfp.h" #include #include #include #include #include // // ---------------------------------------------------------------- Definitions // #define DWARF_LINE_IS_STATEMENT 0x00000001 #define DWARF_LINE_BASIC_BLOCK 0x00000002 #define DWARF_LINE_END_SEQUENCE 0x00000004 #define DWARF_LINE_PROLOGUE_END 0x00000008 #define DWARF_LINE_EPILOGUE_BEGIN 0x00000010 #define DWARF_INITIAL_INCLUDE_FILE_CAPACITY 4 // // ------------------------------------------------------ Data Type Definitions // /*++ Structure Description: This structure stores the state of a row in the DWARF line number matrix. Members: Address - Stores the current address. OpIndex - Stores the operation index for Very Long Instruction Word instructions. For most architectures this is always zero. File - Stores the file index. Line - Stores the one-based line number. Column - Stores the column number. Flags - Stores the bitfield of flags. See DWARF_LINE_* definitions. Isa - Stores the instruction set identifier. Zero indicates the architecturally defined instruction set. Discriminator - Stores the discriminator for multiple blocks that share the same file, line, and column. Most of the time this is zero. --*/ typedef struct _DWARF_LINE { ULONGLONG Address; ULONG OpIndex; ULONG File; ULONG Line; ULONG Column; ULONG Flags; ULONG Isa; ULONG Discriminator; } DWARF_LINE, *PDWARF_LINE; /*++ Structure Description: This structure stores the state machine context for a DWARF line number program. Members: Registers - Stores the line registers. PreviousLine - Stores the previously emitted line number. --*/ typedef struct _DWARF_LINE_STATE { DWARF_LINE Registers; PSOURCE_LINE_SYMBOL PreviousLine; } DWARF_LINE_STATE, *PDWARF_LINE_STATE; /*++ Structure Description: This structure stores the information for a source file in the DWARF line number program. Members: Path - Stores a pointer to a string containing the relative or absolute path to the file. DirectoryIndex - Stores the index of the include directory this file lives in. ModificationDate - Stores the modification date of the file when it was read by the compiler. FileSize - Stores the size of the file when it was read by the compiler. FileSymbol - Stores a cached pointer to the source file symbol. --*/ typedef struct _DWARF_LINE_FILE { PSTR Path; DWARF_LEB128 DirectoryIndex; DWARF_LEB128 ModificationDate; DWARF_LEB128 FileSize; PSOURCE_FILE_SYMBOL FileSymbol; } DWARF_LINE_FILE, *PDWARF_LINE_FILE; /*++ Structure Description: This structure stores the header of a line table program. Members: UnitLength - Stores the size in bytes of the line number information for this compilation unit, not including this field. Is64Bit - Stores a boolean indicating whether this table is 64-bit or not. Version - Stores the table version number. HeaderLength - Stores the number of bytes following the header length field to the beginning of the first byte of the line number program itself. This is 4/8 bytes depending on whether 64-bit mode is in effect. MinimumInstructionLength - Stores the size in bytes of the smallest target machine instruction. Line number program opcodes that alter the address and op-index registers use this and the maximum operations per instruction member in their calculations. MaximumOperationsPerInstruction - Stores the maximum number of individual operations that may be encoded in an instruction. For non-VLIW architectures, this is 1, and the operation pointer is always the address register. DefaultIsStatement - Stores a boolean indicating the initial value of the is-statement flag. LineBase - Stores a parameter used in calculating the effects of special offsets. LineRange - Stores another parameter used in calculating the effects of special offsets. OpcodeBase - Stores the number assigned to the first special opcode (one higher than the highest standard opcode implemented). StandardOpcodeLengths - Stores the number of LEB128 operands for each of the standard opcodes. The first element of the array corresponds to opcode 1, and the last element corresponds to OpcodeBase - 1. IncludeDirectories - Stores an array of include paths that the compiler searched when generating this compilation unit. IncludeDirectoryCount - Stores the number of elements in the include directories array, including the space left for the zeroth entry. Files - Stores an array of include files. FileCount - Stores the number of elements in the files array. ProgramStart - Stores a pointer to the start of the line number program. End - Stores the end of this table. --*/ typedef struct _DWARF_LINE_TABLE_HEADER { ULONGLONG UnitLength; BOOL Is64Bit; USHORT Version; ULONGLONG HeaderLength; UCHAR MinimumInstructionLength; UCHAR MaximumOperationsPerInstruction; UCHAR DefaultIsStatement; CHAR LineBase; UCHAR LineRange; UCHAR OpcodeBase; UCHAR StandardOpcodeLengths[32]; PSTR *IncludeDirectories; UINTN IncludeDirectoryCount; PDWARF_LINE_FILE Files; UINTN FileCount; PUCHAR ProgramStart; PUCHAR End; } DWARF_LINE_TABLE_HEADER, *PDWARF_LINE_TABLE_HEADER; // // ----------------------------------------------- Internal Function Prototypes // INT DwarfpProcessLineTable ( PDWARF_CONTEXT Context, PSTR CompileDirectory, ULONG AddressSize, PUCHAR *Table, PUCHAR End ); INT DwarfpEmitLine ( PDWARF_CONTEXT Context, PDWARF_LINE_TABLE_HEADER Header, PDWARF_LINE_STATE State ); INT DwarfpReadLineNumberHeader ( PUCHAR *Table, PUCHAR End, PDWARF_LINE_TABLE_HEADER Header ); // // -------------------------------------------------------------------- Globals // PSTR DwarfLineStandardOpNames[] = { "DwarfLnsCopy", "DwarfLnsAdvancePc", "DwarfLnsAdvanceLine", "DwarfLnsSetFile", "DwarfLnsSetColumn", "DwarfLnsNegateStatement", "DwarfLnsSetBasicBlock", "DwarfLnsConstAddPc", "DwarfLnsFixedAdvancePc", "DwarfLnsSetPrologueEnd", "DwarfLnsSetEpilogueBegin", "DwarfLnsSetIsa" }; PSTR DwarfLineExtendedOpNames[] = { "DwarfLneEndSequence", "DwarfLneSetAddress", "DwarfLneDefineFile", "DwarfLneSetDiscriminator", }; // // ------------------------------------------------------------------ Functions // INT DwarfpProcessStatementList ( PDWARF_CONTEXT Context, PDWARF_DIE Die ) /*++ Routine Description: This routine is called on a compile unit DIE to process the line numbers. Arguments: Context - Supplies a pointer to the DWARF context. Die - Supplies a pointer to the compile unit DIE. Return Value: 0 on success. Returns an error number on failure. --*/ { PSTR CompileDirectory; PUCHAR End; PDWARF_LOADING_CONTEXT LoadingContext; ULONGLONG Offset; PDWARF_ATTRIBUTE_VALUE OffsetAttribute; INT Status; PUCHAR Table; PDWARF_COMPILATION_UNIT Unit; assert(Die->Tag == DwarfTagCompileUnit); LoadingContext = Context->LoadingContext; Unit = LoadingContext->CurrentUnit; CompileDirectory = DwarfpGetStringAttribute(Die, DwarfAtCompDir); OffsetAttribute = DwarfpGetAttribute(Die, DwarfAtStatementList); if (OffsetAttribute == NULL) { return 0; } if (!DWARF_SECTION_OFFSET_FORM(OffsetAttribute->Form, Unit)) { return 0; } Offset = OffsetAttribute->Value.Offset; Table = Context->Sections.Lines.Data; End = Table + Context->Sections.Lines.Size; if ((Table == NULL) || (Table == End) || (Table + Offset >= End)) { DWARF_ERROR("DWARF: Missing line number information.\n"); return 0; } Table += Offset; Status = DwarfpProcessLineTable(Context, CompileDirectory, Unit->AddressSize, &Table, End); return Status; } // // --------------------------------------------------------- Internal Functions // INT DwarfpProcessLineTable ( PDWARF_CONTEXT Context, PSTR CompileDirectory, ULONG AddressSize, PUCHAR *Table, PUCHAR End ) /*++ Routine Description: This routine processes a single compilation unit's DWARF line table. Arguments: Context - Supplies a pointer to the DWARF context. CompileDirectory - Supplies a pointer to the compilation unit's directory. AddressSize - Supplies the size of an address on the target. Table - Supplies a pointer that on input contains a pointer to the start of the section. On output this pointer will be advanced past the fields scanned. End - Supplies a pointer to the end of the section. Return Value: 0 on success. Returns an error number on failure. --*/ { UINTN AllocationSize; PDWARF_LINE_FILE File; DWARF_LINE_TABLE_HEADER Header; UINTN Index; ULONG InitialFlags; PVOID NewBuffer; PUCHAR Next; UCHAR Op; DWARF_LEB128 Operand; ULONG OperationAdvance; DWARF_SLEB128 SignedOperand; DWARF_LEB128 Size; PUCHAR Start; DWARF_LINE_STATE State; INT Status; // // Read and and potentially print out the header. // memset(&Header, 0, sizeof(DWARF_LINE_TABLE_HEADER)); Start = *Table; Status = DwarfpReadLineNumberHeader(Table, End, &Header); if (Status != 0) { goto ProcessLineTableEnd; } assert(Header.IncludeDirectoryCount >= 1); Header.IncludeDirectories[0] = CompileDirectory; if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("Line Table at offset %x\n" " UnitLength %I64x %s\n" " Version %d\n" " Header Length 0x%I64x\n" " Minimum Instruction Length %d\n" " Default Is Statement: %d\n" " Line Base: %d\n" " Line Range: %d\n" " Opcode Base: %d\n\n" " Opcodes:\n", (PVOID)Start - Context->Sections.Lines.Data, Header.UnitLength, Header.Is64Bit ? "64-bit" : "32-bit", Header.Version, Header.HeaderLength, Header.MinimumInstructionLength, Header.DefaultIsStatement, Header.LineBase, Header.LineRange, Header.OpcodeBase); for (Index = 1; Index < Header.OpcodeBase; Index += 1) { DWARF_PRINT(" Opcode %d: %d arguments.\n", Index, Header.StandardOpcodeLengths[Index]); } DWARF_PRINT("\n Directory Table:\n"); for (Index = 0; Index < Header.IncludeDirectoryCount; Index += 1) { DWARF_PRINT(" %d: %s\n", Index, Header.IncludeDirectories[Index]); } DWARF_PRINT("\n File Table\n Index: Directory Time Size Name\n"); for (Index = 0; Index < Header.FileCount; Index += 1) { DWARF_PRINT(" %d: %I64d %I64x %I64d %s\n", Index + 1, Header.Files[Index].DirectoryIndex, Header.Files[Index].ModificationDate, Header.Files[Index].FileSize, Header.Files[Index].Path); } DWARF_PRINT("\n Line Statements:\n"); } // // Initialize the state machine registers. // InitialFlags = 0; if (Header.DefaultIsStatement != FALSE) { InitialFlags |= DWARF_LINE_IS_STATEMENT; } memset(&State, 0, sizeof(DWARF_LINE_STATE)); State.Registers.File = 1; State.Registers.Line = 1; State.Registers.Flags = InitialFlags; assert(Header.MaximumOperationsPerInstruction != 0); // // Loop decoding the line program. // *Table = Header.ProgramStart; while (*Table < Header.End) { Op = DwarfpRead1(Table); // // Most opcodes are special opcodes. Each special opcode does the same // thing, they only differ in how far they advance the address/opindex // and line. How far they advance depends on values in the header that // are tuned per architecture, and the formulas below which come out of // the DWARF4 specification. // if (Op >= Header.OpcodeBase) { Op -= Header.OpcodeBase; if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT(" Special op %d ", Op); } OperationAdvance = Op / Header.LineRange; State.Registers.Address += Header.MinimumInstructionLength * ((State.Registers.OpIndex + OperationAdvance) / Header.MaximumOperationsPerInstruction); State.Registers.OpIndex = (State.Registers.OpIndex + OperationAdvance) % Header.MaximumOperationsPerInstruction; State.Registers.Line += Header.LineBase + (Op % Header.LineRange); Status = DwarfpEmitLine(Context, &Header, &State); if (Status != 0) { goto ProcessLineTableEnd; } State.Registers.Flags &= ~(DWARF_LINE_BASIC_BLOCK | DWARF_LINE_PROLOGUE_END | DWARF_LINE_EPILOGUE_BEGIN); // // If the first byte is zero, this is an extended opcode. // } else if (Op == 0) { Size = DwarfpReadLeb128(Table); Next = *Table + Size; Op = DwarfpRead1(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { if (Op <= DwarfLneSetDiscriminator) { DWARF_PRINT(" Extended: %s ", DwarfLineExtendedOpNames[Op - 1]); } else if (Op >= DwarfLneLowUser) { DWARF_PRINT(" Extended: User%d ", Op); } else { DWARF_PRINT(" Standard: Unknown%d ", Op); } } switch (Op) { // // Emit one more row using the current registers, set the end // sequence boolean in the registers. Then reset the state machine // and move to the next sequence. // case DwarfLneEndSequence: if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT(" End Sequence\n "); } State.Registers.Flags |= DWARF_LINE_END_SEQUENCE; Status = DwarfpEmitLine(Context, &Header, &State); if (Status != 0) { goto ProcessLineTableEnd; } memset(&State, 0, sizeof(DWARF_LINE_STATE)); State.Registers.File = 1; State.Registers.Line = 1; State.Registers.Flags = InitialFlags; break; // // Set the address to a relocatable target-address-sized address. // case DwarfLneSetAddress: if (AddressSize == 8) { State.Registers.Address = DwarfpRead8(Table); } else { assert(AddressSize == 4); State.Registers.Address = DwarfpRead4(Table); } if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%I64x ", State.Registers.Address); } break; // // Create a new file in the array. // case DwarfLneDefineFile: AllocationSize = (Header.FileCount + 1) * sizeof(DWARF_LINE_FILE); NewBuffer = realloc(Header.Files, AllocationSize); if (NewBuffer == NULL) { Status = ENOMEM; goto ProcessLineTableEnd; } Header.Files = NewBuffer; File = &(Header.Files[Header.FileCount]); File->Path = (PSTR)*Table; *Table += strlen(File->Path) + 1; File->DirectoryIndex = DwarfpReadLeb128(Table); File->ModificationDate = DwarfpReadLeb128(Table); File->FileSize = DwarfpReadLeb128(Table); File->FileSymbol = NULL; if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%d: %I64d %I64d %I64d %s ", Header.FileCount, File->DirectoryIndex, File->ModificationDate, File->FileSize, File->Path); } Header.FileCount += 1; break; case DwarfLneSetDiscriminator: State.Registers.Discriminator = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%u ", State.Registers.Discriminator); } break; default: DWARF_ERROR("DWARF: Unknown extended op %d\n", Op); *Table = Next; break; } // // The known instructions should have fully used up the // instructions, and unknown ones should simply be set to jump // over. // assert(*Table == Next); // // If it's less than the special opcode base, then it's a standard // opcode. // } else { if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { if (Op <= DwarfLnsSetIsa) { DWARF_PRINT(" Standard: %s ", DwarfLineStandardOpNames[Op - 1]); } else { DWARF_PRINT(" Standard: Unknown%d ", Op); } } switch (Op) { // // Append a new row to the matrix using the current values of the // registers. Then reset the booleans. // case DwarfLnsCopy: assert(Header.StandardOpcodeLengths[Op] == 0); Status = DwarfpEmitLine(Context, &Header, &State); if (Status != 0) { goto ProcessLineTableEnd; } State.Registers.Discriminator = 0; State.Registers.Flags &= ~(DWARF_LINE_BASIC_BLOCK | DWARF_LINE_PROLOGUE_END | DWARF_LINE_EPILOGUE_BEGIN); break; // // Get an single LEB128 operand, and advance the PC and op-index // (but not the line) the way the special opcodes do. // case DwarfLnsAdvancePc: assert(Header.StandardOpcodeLengths[Op] == 1); Operand = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%I64u ", Operand); } State.Registers.Address += Header.MinimumInstructionLength * ((State.Registers.OpIndex + Operand) / Header.MaximumOperationsPerInstruction); State.Registers.OpIndex = (State.Registers.OpIndex + Operand) % Header.MaximumOperationsPerInstruction; break; // // Simply add the operand to the line. // case DwarfLnsAdvanceLine: assert(Header.StandardOpcodeLengths[Op] == 1); SignedOperand = DwarfpReadSleb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%I64d ", SignedOperand); } State.Registers.Line += SignedOperand; break; // // Set a new file. // case DwarfLnsSetFile: assert(Header.StandardOpcodeLengths[Op] == 1); State.Registers.File = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%u ", State.Registers.File); } break; // // Set a new column. // case DwarfLnsSetColumn: assert(Header.StandardOpcodeLengths[Op] == 1); State.Registers.Column = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%u ", State.Registers.Column); } break; // // Toggle the is statement flag. // case DwarfLnsNegateStatement: assert(Header.StandardOpcodeLengths[Op] == 0); State.Registers.Flags ^= DWARF_LINE_IS_STATEMENT; break; // // Set the basic block flag. // case DwarfLnsSetBasicBlock: assert(Header.StandardOpcodeLengths[Op] == 0); State.Registers.Flags |= DWARF_LINE_BASIC_BLOCK; break; // // Advance the address and op-index by the increments corresponding // to special register 255 (but don't touch the line number). This // allows a slightly more efficient "fast-forward" of the PC. // case DwarfLnsConstAddPc: assert(Header.StandardOpcodeLengths[Op] == 0); OperationAdvance = (255 - Header.OpcodeBase) / Header.LineRange; State.Registers.Address += Header.MinimumInstructionLength * ((State.Registers.OpIndex + OperationAdvance) / Header.MaximumOperationsPerInstruction); State.Registers.OpIndex = (State.Registers.OpIndex + OperationAdvance) % Header.MaximumOperationsPerInstruction; break; // // Advance the PC by a fixed size operand and clear op-index. This // is supported for simpler assemblers that cannot emit special // opcodes. // case DwarfLnsFixedAdvancePc: assert(Header.StandardOpcodeLengths[Op] == 1); Operand = DwarfpRead2(Table); State.Registers.Address += Operand; if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%u ", (USHORT)Operand); } State.Registers.OpIndex = 0; break; // // Set the prologue end flag. // case DwarfLnsSetPrologueEnd: assert(Header.StandardOpcodeLengths[Op] == 0); State.Registers.Flags |= DWARF_LINE_PROLOGUE_END; break; // // Set the epilogue begin flag. // case DwarfLnsSetEpilogueBegin: assert(Header.StandardOpcodeLengths[Op] == 0); State.Registers.Flags |= DWARF_LINE_EPILOGUE_BEGIN; break; // // Set the current instruction set. // case DwarfLnsSetIsa: assert(Header.StandardOpcodeLengths[Op] == 1); State.Registers.Isa = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%u ", State.Registers.Isa); } break; // // Advance over the instruction without knowing what it does. // default: Index = Header.StandardOpcodeLengths[Op]; while (Index != 0) { Operand = DwarfpReadLeb128(Table); if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("%I64x ", Operand); } } break; } } if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("\n"); } } ProcessLineTableEnd: if (Header.IncludeDirectories != NULL) { free(Header.IncludeDirectories); } if (Header.Files != NULL) { free(Header.Files); } return Status; } INT DwarfpEmitLine ( PDWARF_CONTEXT Context, PDWARF_LINE_TABLE_HEADER Header, PDWARF_LINE_STATE State ) /*++ Routine Description: This routine emits a line number symbol based on the current DWARF line program state. Arguments: Context - Supplies a pointer to the DWARF context. Header - Supplies a pointer to the line table header. State - Supplies a pointer to the current line state. Return Value: 0 on success. Returns an error number on failure. --*/ { UINTN DirectoryIndex; PSOURCE_FILE_SYMBOL File; ULONG FileIndex; ULONG Flags; PSOURCE_LINE_SYMBOL Line; PDWARF_LINE_FILE LineFile; PDWARF_LOADING_CONTEXT LoadingContext; LoadingContext = Context->LoadingContext; assert(LoadingContext->CurrentFile != NULL); // // First, find the source file symbol, creating it if it does not exist. // assert(State->Registers.File != 0); FileIndex = State->Registers.File - 1; if (FileIndex >= Header->FileCount) { DWARF_ERROR("DWARF: File index %d bigger than count %d.\n", FileIndex, Header->FileCount); return ERANGE; } LineFile = &(Header->Files[FileIndex]); if (LineFile->FileSymbol != NULL) { File = LineFile->FileSymbol; } else { DirectoryIndex = LineFile->DirectoryIndex; if (DirectoryIndex >= Header->IncludeDirectoryCount) { DWARF_ERROR("DWARF: Directory index %d bigger than count %d.\n", DirectoryIndex, Header->IncludeDirectoryCount); return ERANGE; } File = DwarfpFindSource(Context, Header->IncludeDirectories[DirectoryIndex], LineFile->Path, TRUE); if (File == NULL) { return ENOMEM; } LineFile->FileSymbol = File; } if ((Context->Flags & DWARF_CONTEXT_DEBUG_LINE_NUMBERS) != 0) { DWARF_PRINT("\n Emit: %s/%s:%d %I64x ", File->SourceDirectory, File->SourceFile, State->Registers.Line, State->Registers.Address); if (State->Registers.Column != 0) { DWARF_PRINT("Column %d ", State->Registers.Column); } if (State->Registers.OpIndex != 0) { DWARF_PRINT("OpIndex %d ", State->Registers.OpIndex); } if (State->Registers.Discriminator != 0) { DWARF_PRINT("Disc %d ", State->Registers.Discriminator); } if (State->Registers.Isa != 0) { DWARF_PRINT("Isa %d ", State->Registers.Isa); } Flags = State->Registers.Flags; if ((Flags & DWARF_LINE_IS_STATEMENT) != 0) { DWARF_PRINT("Stmt "); } if ((Flags & DWARF_LINE_BASIC_BLOCK) != 0) { DWARF_PRINT("BasicBlock "); } if ((Flags & DWARF_LINE_PROLOGUE_END) != 0) { DWARF_PRINT("PrologueEnd "); } if ((Flags & DWARF_LINE_EPILOGUE_BEGIN) != 0) { DWARF_PRINT("EpilogueBegin "); } if ((Flags & DWARF_LINE_END_SEQUENCE) != 0) { DWARF_PRINT("End "); } } // // Set the end address of the previous line if there is one. // if (State->PreviousLine != NULL) { State->PreviousLine->End = State->Registers.Address; } // // If it's the end sequence, don't create a new line. // if ((State->Registers.Flags & DWARF_LINE_END_SEQUENCE) != 0) { return 0; } // // TODO: Change StartOffset and EndOffset to be absolute always, and // remove the owning function from the line symbol. // Line = malloc(sizeof(SOURCE_LINE_SYMBOL)); if (Line == NULL) { return ENOMEM; } memset(Line, 0, sizeof(SOURCE_LINE_SYMBOL)); Line->ParentSource = File; Line->LineNumber = State->Registers.Line; Line->Start = State->Registers.Address; Line->End = Line->Start + 1; State->PreviousLine = Line; INSERT_BEFORE(&(Line->ListEntry), &(LoadingContext->CurrentFile->SourceLinesHead)); return 0; } INT DwarfpReadLineNumberHeader ( PUCHAR *Table, PUCHAR End, PDWARF_LINE_TABLE_HEADER Header ) /*++ Routine Description: This routine reads the DWARF line number program header out of the section. Arguments: Table - Supplies a pointer that on input contains a pointer to the start of the section. On output this pointer will be advanced past the fields scanned. End - Supplies a pointer to the end of the section. Header - Supplies a pointer where the header details will be returned on success. Return Value: 0 on success. Returns an error number on failure. --*/ { UINTN Capacity; UINTN Count; PDWARF_LINE_FILE File; PDWARF_LINE_FILE Files; UINTN Index; PVOID NewBuffer; PSTR String; DwarfpReadInitialLength(Table, &(Header->Is64Bit), &(Header->UnitLength)); Header->End = *Table + Header->UnitLength; Header->Version = DwarfpRead2(Table); Header->HeaderLength = DWARF_READN(Table, Header->Is64Bit); Header->ProgramStart = *Table + Header->HeaderLength; Header->MinimumInstructionLength = DwarfpRead1(Table); Header->MaximumOperationsPerInstruction = 1; if (Header->Version >= 4) { Header->MaximumOperationsPerInstruction = DwarfpRead1(Table); } Header->DefaultIsStatement = DwarfpRead1(Table); Header->LineBase = DwarfpRead1(Table); Header->LineRange = DwarfpRead1(Table); Header->OpcodeBase = DwarfpRead1(Table); // // If too many more standard opcodes are introduced, this structure will // need to be adjusted. // assert(Header->OpcodeBase < sizeof(Header->StandardOpcodeLengths)); // // Opcode zero is ignored since it's the opcode to introduce extended // opcodes. Gather the parameter counts for the standard opcodes. // Header->StandardOpcodeLengths[0] = -1; for (Index = 1; Index < Header->OpcodeBase; Index += 1) { Header->StandardOpcodeLengths[Index] = DwarfpRead1(Table); } // // Count the include directories, terminated by a null byte. The zeroth // entry is always the current compilation unit's compile directory. // Count = 1; String = (PSTR)*Table; while (*String != '\0') { Count += 1; String += strlen(String) + 1; } Header->IncludeDirectories = malloc((Count + 1) * sizeof(PSTR)); if (Header->IncludeDirectories == NULL) { return ENOMEM; } memset(Header->IncludeDirectories, 0, (Count + 1) * sizeof(PSTR)); // // Fill in the array of include directories. // String = (PSTR)*Table; Index = 1; while (*String != '\0') { Header->IncludeDirectories[Index] = String; Index += 1; String += strlen(String) + 1; } Header->IncludeDirectoryCount = Index; *Table = (PUCHAR)String + 1; // // Now fill in the array of file entries. // Capacity = 0; Count = 0; Files = NULL; while (**Table != '\0') { // // Allocate a new entry if needed. // if (Capacity <= Count) { if (Capacity == 0) { Capacity = DWARF_INITIAL_INCLUDE_FILE_CAPACITY; } else { Capacity *= 2; } NewBuffer = realloc(Files, Capacity * sizeof(DWARF_LINE_FILE)); if (NewBuffer == NULL) { if (Files != NULL) { free(Files); } return ENOMEM; } Files = NewBuffer; } File = &(Files[Count]); File->Path = (PSTR)*Table; *Table += strlen(File->Path) + 1; File->DirectoryIndex = DwarfpReadLeb128(Table); File->ModificationDate = DwarfpReadLeb128(Table); File->FileSize = DwarfpReadLeb128(Table); File->FileSymbol = NULL; Count += 1; } Header->Files = Files; Header->FileCount = Count; return 0; }