diff --git a/llvm/include/llvm/ObjectYAML/DWARFYAML.h b/llvm/include/llvm/ObjectYAML/DWARFYAML.h --- a/llvm/include/llvm/ObjectYAML/DWARFYAML.h +++ b/llvm/include/llvm/ObjectYAML/DWARFYAML.h @@ -137,9 +137,9 @@ struct LineTable { dwarf::DwarfFormat Format; - uint64_t Length; + Optional Length; uint16_t Version; - uint64_t PrologueLength; + Optional PrologueLength; uint8_t MinInstLength; uint8_t MaxOpsPerInst; uint8_t DefaultIsStmt; diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -448,89 +448,114 @@ encodeULEB128(File.Length, OS); } +static void writeLineTableOpcode(const DWARFYAML::LineTableOpcode &Op, + uint8_t OpcodeBase, uint8_t AddrSize, + raw_ostream &OS, bool IsLittleEndian) { + writeInteger((uint8_t)Op.Opcode, OS, IsLittleEndian); + if (Op.Opcode == 0) { + encodeULEB128(Op.ExtLen, OS); + writeInteger((uint8_t)Op.SubOpcode, OS, IsLittleEndian); + switch (Op.SubOpcode) { + case dwarf::DW_LNE_set_address: + cantFail( + writeVariableSizedInteger(Op.Data, AddrSize, OS, IsLittleEndian)); + break; + case dwarf::DW_LNE_define_file: + emitFileEntry(OS, Op.FileEntry); + break; + case dwarf::DW_LNE_set_discriminator: + encodeULEB128(Op.Data, OS); + break; + case dwarf::DW_LNE_end_sequence: + break; + default: + for (auto OpByte : Op.UnknownOpcodeData) + writeInteger((uint8_t)OpByte, OS, IsLittleEndian); + } + } else if (Op.Opcode < OpcodeBase) { + switch (Op.Opcode) { + case dwarf::DW_LNS_copy: + case dwarf::DW_LNS_negate_stmt: + case dwarf::DW_LNS_set_basic_block: + case dwarf::DW_LNS_const_add_pc: + case dwarf::DW_LNS_set_prologue_end: + case dwarf::DW_LNS_set_epilogue_begin: + break; + + case dwarf::DW_LNS_advance_pc: + case dwarf::DW_LNS_set_file: + case dwarf::DW_LNS_set_column: + case dwarf::DW_LNS_set_isa: + encodeULEB128(Op.Data, OS); + break; + + case dwarf::DW_LNS_advance_line: + encodeSLEB128(Op.SData, OS); + break; + + case dwarf::DW_LNS_fixed_advance_pc: + writeInteger((uint16_t)Op.Data, OS, IsLittleEndian); + break; + + default: + for (auto OpData : Op.StandardOpcodeData) { + encodeULEB128(OpData, OS); + } + } + } +} + Error DWARFYAML::emitDebugLine(raw_ostream &OS, const DWARFYAML::Data &DI) { - for (const auto &LineTable : DI.DebugLines) { - writeInitialLength(LineTable.Format, LineTable.Length, OS, - DI.IsLittleEndian); - writeInteger((uint16_t)LineTable.Version, OS, DI.IsLittleEndian); - writeDWARFOffset(LineTable.PrologueLength, LineTable.Format, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.MinInstLength, OS, DI.IsLittleEndian); + for (const DWARFYAML::LineTable &LineTable : DI.DebugLines) { + // Buffer holds the bytes following the header_length (or prologue_length in + // DWARFv2) field to the end of the line number program itself. + std::string Buffer; + raw_string_ostream BufferOS(Buffer); + + writeInteger((uint8_t)LineTable.MinInstLength, BufferOS, DI.IsLittleEndian); + // TODO: Add support for emitting DWARFv5 line table. if (LineTable.Version >= 4) - writeInteger((uint8_t)LineTable.MaxOpsPerInst, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.DefaultIsStmt, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.LineBase, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.LineRange, OS, DI.IsLittleEndian); - writeInteger((uint8_t)LineTable.OpcodeBase, OS, DI.IsLittleEndian); - - for (auto OpcodeLength : LineTable.StandardOpcodeLengths) - writeInteger((uint8_t)OpcodeLength, OS, DI.IsLittleEndian); - - for (auto IncludeDir : LineTable.IncludeDirs) { - OS.write(IncludeDir.data(), IncludeDir.size()); - OS.write('\0'); + writeInteger((uint8_t)LineTable.MaxOpsPerInst, BufferOS, + DI.IsLittleEndian); + writeInteger((uint8_t)LineTable.DefaultIsStmt, BufferOS, DI.IsLittleEndian); + writeInteger((uint8_t)LineTable.LineBase, BufferOS, DI.IsLittleEndian); + writeInteger((uint8_t)LineTable.LineRange, BufferOS, DI.IsLittleEndian); + writeInteger((uint8_t)LineTable.OpcodeBase, BufferOS, DI.IsLittleEndian); + + for (uint8_t OpcodeLength : LineTable.StandardOpcodeLengths) + writeInteger((uint8_t)OpcodeLength, BufferOS, DI.IsLittleEndian); + + for (StringRef IncludeDir : LineTable.IncludeDirs) { + BufferOS.write(IncludeDir.data(), IncludeDir.size()); + BufferOS.write('\0'); } - OS.write('\0'); + BufferOS.write('\0'); - for (auto File : LineTable.Files) - emitFileEntry(OS, File); - OS.write('\0'); + for (const DWARFYAML::File &File : LineTable.Files) + emitFileEntry(BufferOS, File); + BufferOS.write('\0'); - uint8_t AddrSize = DI.Is64BitAddrSize ? 8 : 4; - - for (auto Op : LineTable.Opcodes) { - writeInteger((uint8_t)Op.Opcode, OS, DI.IsLittleEndian); - if (Op.Opcode == 0) { - encodeULEB128(Op.ExtLen, OS); - writeInteger((uint8_t)Op.SubOpcode, OS, DI.IsLittleEndian); - switch (Op.SubOpcode) { - case dwarf::DW_LNE_set_address: - cantFail(writeVariableSizedInteger(Op.Data, AddrSize, OS, - DI.IsLittleEndian)); - break; - case dwarf::DW_LNE_define_file: - emitFileEntry(OS, Op.FileEntry); - break; - case dwarf::DW_LNE_set_discriminator: - encodeULEB128(Op.Data, OS); - break; - case dwarf::DW_LNE_end_sequence: - break; - default: - for (auto OpByte : Op.UnknownOpcodeData) - writeInteger((uint8_t)OpByte, OS, DI.IsLittleEndian); - } - } else if (Op.Opcode < LineTable.OpcodeBase) { - switch (Op.Opcode) { - case dwarf::DW_LNS_copy: - case dwarf::DW_LNS_negate_stmt: - case dwarf::DW_LNS_set_basic_block: - case dwarf::DW_LNS_const_add_pc: - case dwarf::DW_LNS_set_prologue_end: - case dwarf::DW_LNS_set_epilogue_begin: - break; - - case dwarf::DW_LNS_advance_pc: - case dwarf::DW_LNS_set_file: - case dwarf::DW_LNS_set_column: - case dwarf::DW_LNS_set_isa: - encodeULEB128(Op.Data, OS); - break; - - case dwarf::DW_LNS_advance_line: - encodeSLEB128(Op.SData, OS); - break; - - case dwarf::DW_LNS_fixed_advance_pc: - writeInteger((uint16_t)Op.Data, OS, DI.IsLittleEndian); - break; - - default: - for (auto OpData : Op.StandardOpcodeData) { - encodeULEB128(OpData, OS); - } - } - } + uint64_t HeaderLength = + LineTable.PrologueLength ? *LineTable.PrologueLength : Buffer.size(); + + for (const DWARFYAML::LineTableOpcode &Op : LineTable.Opcodes) + writeLineTableOpcode(Op, LineTable.OpcodeBase, DI.Is64BitAddrSize ? 8 : 4, + BufferOS, DI.IsLittleEndian); + + uint64_t Length; + if (LineTable.Length) { + Length = *LineTable.Length; + } else { + Length = 2; // sizeof(version) + Length += + (LineTable.Format == dwarf::DWARF64 ? 8 : 4); // sizeof(header_length) + Length += Buffer.size(); } + + writeInitialLength(LineTable.Format, Length, OS, DI.IsLittleEndian); + writeInteger((uint16_t)LineTable.Version, OS, DI.IsLittleEndian); + writeDWARFOffset(HeaderLength, LineTable.Format, OS, DI.IsLittleEndian); + OS.write(Buffer.data(), Buffer.size()); } return Error::success(); diff --git a/llvm/lib/ObjectYAML/DWARFYAML.cpp b/llvm/lib/ObjectYAML/DWARFYAML.cpp --- a/llvm/lib/ObjectYAML/DWARFYAML.cpp +++ b/llvm/lib/ObjectYAML/DWARFYAML.cpp @@ -230,9 +230,9 @@ void MappingTraits::mapping( IO &IO, DWARFYAML::LineTable &LineTable) { IO.mapOptional("Format", LineTable.Format, dwarf::DWARF32); - IO.mapRequired("Length", LineTable.Length); + IO.mapOptional("Length", LineTable.Length); IO.mapRequired("Version", LineTable.Version); - IO.mapRequired("PrologueLength", LineTable.PrologueLength); + IO.mapOptional("PrologueLength", LineTable.PrologueLength); IO.mapRequired("MinInstLength", LineTable.MinInstLength); if(LineTable.Version >= 4) IO.mapRequired("MaxOpsPerInst", LineTable.MaxOpsPerInst); diff --git a/llvm/test/tools/yaml2obj/ELF/DWARF/debug-line.yaml b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-line.yaml --- a/llvm/test/tools/yaml2obj/ELF/DWARF/debug-line.yaml +++ b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-line.yaml @@ -479,3 +479,115 @@ ExtLen: 0x09 SubOpcode: DW_LNE_set_discriminator Data: 0x1234 + +## j) Test that yaml2obj is able to infer the length and header_length fields. + +# RUN: yaml2obj --docnum=10 %s -o %t10.o +# RUN: llvm-readelf --hex-dump=.debug_line %t10.o | FileCheck %s --check-prefix=INFER-LENGTH + +# INFER-LENGTH: Hex dump of section '.debug_line': +# INFER-LENGTH-NEXT: 0x00000000 2e000000 04002500 00000101 01fb0e04 ......%......... +## ^------- unit_length (4-byte) 0x2e +## ^--- version (2-byte) +## ^-------- header_length (4-byte) 0x25 +## ^- minimum_instruction_length (1-byte) +## ^- maximum_operations_per_instruction (1-byte) +## ^- default_is_stmt (1-byte) +## ^- line_base (1-byte) -5 +## ^- line_range (1-byte) +## ^- opcode_base (1-byte) +# INFER-LENGTH-NEXT: 0x00000010 00010174 656d7031 0074656d 70320000 ...temp1.temp2.. +## ^----- standard_opcode_lengths (3-byte) +## ^------------- include_directories[1] "temp1\0" +## ^------------ include_directories[2] "temp1\0" +## ^- include_directories null byte +# INFER-LENGTH-NEXT: 0x00000020 612e6300 01000062 2e630002 00000000 a.c....b.c...... +## ^------- file_names[1] file name "a.c\0" +## ^- file_names[1] directory index (ULEB128) 0x01 +## ^- file_names[1] file length (ULEB128) 0x00 +## ^- file_names[1] modification time 0x00 +## ^-------- file_names[2] file name "b.c\0" +## ^- file_names[2] directory index (ULEB128) 0x02 +## ^- file_names[2] file length (ULEB128) 0x00 +## ^- file_names[2] modification time 0x00 +## ^- file_names null byte +## ^- DW_LNS_extended_op +# INFER-LENGTH-NEXT: 0x00000030 0101ffff ffff2500 00000000 00000400 ......%......... +## ^- extended op length (ULEB128) 0x01 +## ^- DW_LNE_end_sequence +## ^-------------------------- unit_length (12-byte) +## ^--- version (2-byte) +# INFER-LENGTH-NEXT: 0x00000040 18000000 00000000 010101fb 0e040001 ................ +## ^---------------- header_length (8-byte) +## ^- minimum_instruction_length (1-byte) +## ^- maximum_operations_per_instruction (1-byte) +## ^- default_is_stmt (1-byte) +## ^- line_base (1-byte) -5 +## ^- line_range (1-byte) +## ^- opcode_base (1-byte) +## ^--- standard_opcode_lengths (3-byte) +# INFER-LENGTH-NEXT: 0x00000050 0174656d 70330000 632e6300 01000000 .temp3..c.c..... +## -- +## ^------------ include_directories[1] "temp3\0" +## ^- include_directories null byte +## ^------- file_names[1] file name "a.c\0" +## ^- file_names[1] directory index (ULEB128) 0x01 +## ^- file_names[1] file length (ULEB128) 0x00 +## ^- file_names[1] modification time 0x00 +## ^- file_names null byte +# INFER-LENGTH-NEXT: 0x00000060 000101 ... +## ^- DW_LNS_extended_op +## ^- extended op length (ULEB128) 0x01 +## ^- DW_LNE_end_sequence + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +DWARF: + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 4 + StandardOpcodeLengths: [ 0, 1, 1 ] + IncludeDirs: + - temp1 + - temp2 + Files: + - Name: a.c + DirIdx: 1 + ModTime: 0 + Length: 0 + - Name: b.c + DirIdx: 2 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + - Format: DWARF64 + Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 4 + StandardOpcodeLengths: [ 0, 1, 1 ] + IncludeDirs: + - temp3 + Files: + - Name: c.c + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence diff --git a/llvm/tools/obj2yaml/dwarf2yaml.cpp b/llvm/tools/obj2yaml/dwarf2yaml.cpp --- a/llvm/tools/obj2yaml/dwarf2yaml.cpp +++ b/llvm/tools/obj2yaml/dwarf2yaml.cpp @@ -318,13 +318,15 @@ DebugLines.Format = dwarf::DWARF32; DebugLines.Length = LengthOrDWARF64Prefix; } - uint64_t LineTableLength = DebugLines.Length; + assert(DebugLines.Length); + uint64_t LineTableLength = *DebugLines.Length; uint64_t SizeOfPrologueLength = DebugLines.Format == dwarf::DWARF64 ? 8 : 4; DebugLines.Version = LineData.getU16(&Offset); DebugLines.PrologueLength = LineData.getUnsigned(&Offset, SizeOfPrologueLength); - const uint64_t EndPrologue = DebugLines.PrologueLength + Offset; + assert(DebugLines.PrologueLength); + const uint64_t EndPrologue = *DebugLines.PrologueLength + Offset; DebugLines.MinInstLength = LineData.getU8(&Offset); if (DebugLines.Version >= 4) diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -2196,9 +2196,7 @@ - Value: 0x0000000000000001 - Value: 0x0000000000000000 debug_line: - - Length: 68 - Version: 2 - PrologueLength: 34 + - Version: 2 MinInstLength: 1 DefaultIsStmt: 1 LineBase: 251 @@ -2265,9 +2263,7 @@ - Value: 0x0000000000000001 - Value: 0x0000000000000000 debug_line: - - Length: 61 - Version: 2 - PrologueLength: 34 + - Version: 2 MinInstLength: 1 DefaultIsStmt: 1 LineBase: 251 @@ -2336,9 +2332,7 @@ - Value: 0x0000000000000001 - Value: 0x0000000000000000 debug_line: - - Length: 61 - Version: 2 - PrologueLength: 34 + - Version: 2 MinInstLength: 1 DefaultIsStmt: 1 LineBase: 251 @@ -2408,9 +2402,7 @@ - Value: 0x0000000000000001 - Value: 0x0000000000000000 debug_line: - - Length: 71 - Version: 2 - PrologueLength: 44 + - Version: 2 MinInstLength: 1 DefaultIsStmt: 1 LineBase: 251 @@ -2495,9 +2487,7 @@ - Value: 0x000000000000000D - Value: 0x0000000000000000 debug_line: - - Length: 60 - Version: 2 - PrologueLength: 34 + - Version: 2 MinInstLength: 1 DefaultIsStmt: 1 LineBase: 251