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 @@ -126,7 +126,7 @@ struct LineTableOpcode { dwarf::LineNumberOps Opcode; - uint64_t ExtLen; + Optional ExtLen; dwarf::LineNumberExtendedOps SubOpcode; uint64_t Data; int64_t SData; 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 @@ -480,30 +480,45 @@ encodeULEB128(File.Length, OS); } +static void writeExtendedOpcode(const DWARFYAML::LineTableOpcode &Op, + uint8_t AddrSize, bool IsLittleEndian, + raw_ostream &OS) { + // The first byte of extended opcodes is a zero byte. The next bytes are an + // ULEB128 integer giving the number of bytes in the instruction itself (does + // not include the first zero byte or the size). We serialize the instruction + // itself into the OpBuffer and then write the size of the buffer and the + // buffer to the real output stream. + std::string OpBuffer; + raw_string_ostream OpBufferOS(OpBuffer); + writeInteger((uint8_t)Op.SubOpcode, OpBufferOS, IsLittleEndian); + switch (Op.SubOpcode) { + case dwarf::DW_LNE_set_address: + cantFail(writeVariableSizedInteger(Op.Data, AddrSize, OpBufferOS, + IsLittleEndian)); + break; + case dwarf::DW_LNE_define_file: + emitFileEntry(OpBufferOS, Op.FileEntry); + break; + case dwarf::DW_LNE_set_discriminator: + encodeULEB128(Op.Data, OpBufferOS); + break; + case dwarf::DW_LNE_end_sequence: + break; + default: + for (auto OpByte : Op.UnknownOpcodeData) + writeInteger((uint8_t)OpByte, OpBufferOS, IsLittleEndian); + } + uint64_t ExtLen = Op.ExtLen.getValueOr(OpBuffer.size()); + encodeULEB128(ExtLen, OS); + OS.write(OpBuffer.data(), OpBuffer.size()); +} + 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); - } + writeExtendedOpcode(Op, AddrSize, IsLittleEndian, OS); } else if (Op.Opcode < OpcodeBase) { switch (Op.Opcode) { case dwarf::DW_LNS_copy: 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 @@ -217,7 +217,7 @@ IO &IO, DWARFYAML::LineTableOpcode &LineTableOpcode) { IO.mapRequired("Opcode", LineTableOpcode.Opcode); if (LineTableOpcode.Opcode == dwarf::DW_LNS_extended_op) { - IO.mapRequired("ExtLen", LineTableOpcode.ExtLen); + IO.mapOptional("ExtLen", LineTableOpcode.ExtLen); IO.mapRequired("SubOpcode", LineTableOpcode.SubOpcode); } 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 @@ -616,3 +616,64 @@ LineRange: 14 OpcodeBase: 4 StandardOpcodeLengths: [ 0, 1, 1 ] + +## l) Test that we can specify or omit the ExtLen field of extended opcodes. + +# RUN: yaml2obj --docnum=12 %s -o %t12.o +# RUN: llvm-readelf --hex-dump=.debug_line %t12.o | \ +# RUN: FileCheck %s --check-prefix=EXTLEN + +# EXTLEN: Hex dump of section '.debug_line': +# EXTLEN-NEXT: 0x00000000 20000000 04000800 00000101 01fb0e01 ............... +## ^------- unit_length (4-byte) +## ^--- version (2-byte) +## ^-------- header_length (4-byte) +## ^- minimum_instruction_length (1-byte) +## ^- maximum_operations_per_instruction (1-byte) +## ^- default_is_stmt (1-byte) +## ^- line_base (1-byte) +## ^- line_range (1-byte) +## ^- opcode_base (1-byte) +# EXTLEN-NEXT: 0x00000010 0000000c 03616263 6400b424 b424b424 .....abcd..$.$.$ +## ^- terminator for include_directories (1-byte) +## ^- terminator for file_names (1-byte) +## ^- DW_LNS_extended_op +## ^- extended opcode length (ULEB128) 12 +## ^- DW_LNE_define_file +## ^---------- "abcd\0" +## ^--- directory index (ULEB128) 0x1234 +## ^--- modification time (ULEB128) 0x1234 +## ^--- file length (ULEB128) 0x1234 +# EXTLEN-NEXT: 0x00000020 00b42401 ..$. +## ^- DW_LNS_extended_op +## ^--- extended opcode length (ULEB128) 0x1234 +## ^- 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: 1 + StandardOpcodeLengths: [] + Opcodes: + - Opcode: DW_LNS_extended_op + ## Omit the ExtLen field. + SubOpcode: DW_LNE_define_file + FileEntry: + Name: abcd + DirIdx: 0x1234 + ModTime: 0x1234 + Length: 0x1234 + - Opcode: DW_LNS_extended_op + ## Specify the ExtLen field. + ExtLen: 0x1234 + 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 @@ -419,7 +419,7 @@ case dwarf::DW_LNE_end_sequence: break; default: - while (Offset < StartExt + NewOp.ExtLen) + while (Offset < StartExt + *NewOp.ExtLen) NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset)); } } else if (NewOp.Opcode < DebugLines.OpcodeBase) {