[DWARFv5] Parse new line-table header format.

The directory and file tables now have form-based content descriptors.
Parse these and extract the per-directory/file records based on the
descriptors.  For now we support only DW_FORM_string (inline) for the
path names; follow-up work will add support for indirect forms (i.e.,
DW_FORM_strp, strx<N>, and line_strp).

Differential Revision: http://reviews.llvm.org/D32713

llvm-svn: 301978
This commit is contained in:
Paul Robinson 2017-05-02 21:40:47 +00:00
parent d091e76e0e
commit 2bc3873fe6
5 changed files with 295 additions and 24 deletions

View File

@ -44,6 +44,10 @@ public:
uint64_t TotalLength;
/// Version identifier for the statement information format.
uint16_t Version;
/// In v5, size in bytes of an address (or segment offset).
uint8_t AddressSize;
/// In v5, size in bytes of a segment selector.
uint8_t SegSelectorSize;
/// The number of bytes following the prologue_length field to the beginning
/// of the first byte of the statement program itself.
uint64_t PrologueLength;

View File

@ -10,6 +10,7 @@
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/Format.h"
@ -26,11 +27,19 @@ using namespace llvm;
using namespace dwarf;
typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind;
namespace {
struct ContentDescriptor {
dwarf::LineNumberEntryFormat Type;
dwarf::Form Form;
};
typedef SmallVector<ContentDescriptor, 4> ContentDescriptors;
} // end anonmyous namespace
DWARFDebugLine::Prologue::Prologue() { clear(); }
void DWARFDebugLine::Prologue::clear() {
TotalLength = Version = PrologueLength = 0;
AddressSize = SegSelectorSize = 0;
MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0;
OpcodeBase = 0;
IsDWARF64 = false;
@ -43,6 +52,8 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const {
OS << "Line table prologue:\n"
<< format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength)
<< format(" version: %u\n", Version)
<< format(Version >= 5 ? " address_size: %u\n" : "", AddressSize)
<< format(Version >= 5 ? " seg_select_size: %u\n" : "", SegSelectorSize)
<< format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength)
<< format(" min_inst_length: %u\n", MinInstLength)
<< format(Version >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst)
@ -74,6 +85,125 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS) const {
}
}
// Parse v2-v4 directory and file tables.
static void
parseV2DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr,
uint64_t EndPrologueOffset,
std::vector<StringRef> &IncludeDirectories,
std::vector<DWARFDebugLine::FileNameEntry> &FileNames) {
while (*OffsetPtr < EndPrologueOffset) {
StringRef S = DebugLineData.getCStrRef(OffsetPtr);
if (S.empty())
break;
IncludeDirectories.push_back(S);
}
while (*OffsetPtr < EndPrologueOffset) {
StringRef Name = DebugLineData.getCStrRef(OffsetPtr);
if (Name.empty())
break;
DWARFDebugLine::FileNameEntry FileEntry;
FileEntry.Name = Name;
FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr);
FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr);
FileEntry.Length = DebugLineData.getULEB128(OffsetPtr);
FileNames.push_back(FileEntry);
}
}
// Parse v5 directory/file entry content descriptions.
// Returns the descriptors, or an empty vector if we did not find a path or
// ran off the end of the prologue.
static ContentDescriptors
parseV5EntryFormat(DataExtractor DebugLineData, uint32_t *OffsetPtr,
uint64_t EndPrologueOffset) {
ContentDescriptors Descriptors;
int FormatCount = DebugLineData.getU8(OffsetPtr);
bool HasPath = false;
for (int I = 0; I != FormatCount; ++I) {
if (*OffsetPtr >= EndPrologueOffset)
return ContentDescriptors();
ContentDescriptor Descriptor;
Descriptor.Type =
dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr));
Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr));
if (Descriptor.Type == dwarf::DW_LNCT_path)
HasPath = true;
Descriptors.push_back(Descriptor);
}
return HasPath ? Descriptors : ContentDescriptors();
}
static bool
parseV5DirFileTables(DataExtractor DebugLineData, uint32_t *OffsetPtr,
uint64_t EndPrologueOffset,
std::vector<StringRef> &IncludeDirectories,
std::vector<DWARFDebugLine::FileNameEntry> &FileNames) {
// Get the directory entry description.
ContentDescriptors DirDescriptors =
parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset);
if (DirDescriptors.empty())
return false;
// Get the directory entries, according to the format described above.
int DirEntryCount = DebugLineData.getU8(OffsetPtr);
for (int I = 0; I != DirEntryCount; ++I) {
if (*OffsetPtr >= EndPrologueOffset)
return false;
for (auto Descriptor : DirDescriptors) {
DWARFFormValue Value(Descriptor.Form);
switch (Descriptor.Type) {
case DW_LNCT_path:
if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr))
return false;
IncludeDirectories.push_back(Value.getAsCString().getValue());
break;
default:
if (!Value.skipValue(DebugLineData, OffsetPtr, nullptr))
return false;
}
}
}
// Get the file entry description.
ContentDescriptors FileDescriptors =
parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset);
if (FileDescriptors.empty())
return false;
// Get the file entries, according to the format described above.
int FileEntryCount = DebugLineData.getU8(OffsetPtr);
for (int I = 0; I != FileEntryCount; ++I) {
if (*OffsetPtr >= EndPrologueOffset)
return false;
DWARFDebugLine::FileNameEntry FileEntry;
for (auto Descriptor : FileDescriptors) {
DWARFFormValue Value(Descriptor.Form);
if (!Value.extractValue(DebugLineData, OffsetPtr, nullptr))
return false;
switch (Descriptor.Type) {
case DW_LNCT_path:
FileEntry.Name = Value.getAsCString().getValue();
break;
case DW_LNCT_directory_index:
FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue();
break;
case DW_LNCT_timestamp:
FileEntry.ModTime = Value.getAsUnsignedConstant().getValue();
break;
case DW_LNCT_size:
FileEntry.Length = Value.getAsUnsignedConstant().getValue();
break;
// FIXME: Add MD5
default:
break;
}
}
FileNames.push_back(FileEntry);
}
return true;
}
bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
uint32_t *OffsetPtr) {
const uint64_t PrologueOffset = *OffsetPtr;
@ -90,6 +220,11 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
if (Version < 2)
return false;
if (Version >= 5) {
AddressSize = DebugLineData.getU8(OffsetPtr);
SegSelectorSize = DebugLineData.getU8(OffsetPtr);
}
PrologueLength = DebugLineData.getUnsigned(OffsetPtr, sizeofPrologueLength());
const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr;
MinInstLength = DebugLineData.getU8(OffsetPtr);
@ -106,24 +241,18 @@ bool DWARFDebugLine::Prologue::parse(DataExtractor DebugLineData,
StandardOpcodeLengths.push_back(OpLen);
}
while (*OffsetPtr < EndPrologueOffset) {
StringRef S = DebugLineData.getCStrRef(OffsetPtr);
if (S.empty())
break;
IncludeDirectories.push_back(S);
}
while (*OffsetPtr < EndPrologueOffset) {
StringRef Name = DebugLineData.getCStrRef(OffsetPtr);
if (Name.empty())
break;
FileNameEntry FileEntry;
FileEntry.Name = Name;
FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr);
FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr);
FileEntry.Length = DebugLineData.getULEB128(OffsetPtr);
FileNames.push_back(FileEntry);
}
if (Version >= 5) {
if (!parseV5DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
IncludeDirectories, FileNames)) {
fprintf(stderr,
"warning: parsing line table prologue at 0x%8.8" PRIx64
" found an invalid directory or file table description at"
" 0x%8.8" PRIx64 "\n", PrologueOffset, (uint64_t)*OffsetPtr);
return false;
}
} else
parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset,
IncludeDirectories, FileNames);
if (*OffsetPtr != EndPrologueOffset) {
fprintf(stderr,

View File

@ -1,5 +1,6 @@
# Test object to verify dwarfdump handles v4 and v5 CU/TU headers.
# Test object to verify dwarfdump handles v4 and v5 CU/TU/line headers.
# We have a representative set of units: v4 CU, v5 CU, v4 TU, v5 split TU.
# We have v4 and v5 line-table headers.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-header.s -filetype=obj \
@ -28,6 +29,8 @@ dwo_TU_5:
.byte 0x0e # DW_FORM_strp
.byte 0x03 # DW_AT_name
.byte 0x0e # DW_FORM_strp
.byte 0x10 # DW_AT_stmt_list
.byte 0x17 # DW_FORM_sec_offset
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x02 # Abbrev code
@ -81,10 +84,11 @@ CU_4_version:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
# The compile-unit DIE, which has just DW_AT_producer and DW_AT_name.
# The compile-unit DIE, with DW_AT_producer, DW_AT_name, DW_AT_stmt_list.
.byte 1
.long str_producer
.long str_CU_4
.long LH_4_start
.byte 0 # NULL
CU_4_end:
@ -95,10 +99,11 @@ CU_5_version:
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
# The compile-unit DIE, which has just DW_AT_producer and DW_AT_name.
# The compile-unit DIE, with DW_AT_producer, DW_AT_name, DW_AT_stmt_list.
.byte 1
.long str_producer
.long str_CU_5
.long LH_5_start
.byte 0 # NULL
CU_5_end:
@ -147,3 +152,106 @@ TU_split_5_type:
.byte 0 # NULL
.byte 0 # NULL
TU_split_5_end:
.section .debug_line,"",@progbits
# DWARF v4 line-table header.
LH_4_start:
.long LH_4_end-LH_4_version # Length of Unit
LH_4_version:
.short 4 # DWARF version number
.long LH_4_header_end-LH_4_params # Length of Prologue
LH_4_params:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0 # Standard Opcode Lengths
.byte 1
.byte 1
.byte 1
.byte 1
.byte 0
.byte 0
.byte 0
.byte 1
.byte 0
.byte 0
.byte 1
# Directory table
.asciz "Directory4a"
.asciz "Directory4b"
.byte 0
# File table
.asciz "File4a" # File name 1
.byte 1 # Directory index 1
.byte 0x41 # Timestamp 1
.byte 0x42 # File Size 1
.asciz "File4b" # File name 2
.byte 0 # Directory index 2
.byte 0x43 # Timestamp 2
.byte 0x44 # File Size 2
.byte 0 # End of list
LH_4_header_end:
# Line number program, which is empty.
LH_4_end:
# DWARF v5 line-table header.
LH_5_start:
.long LH_5_end-LH_5_version # Length of Unit
LH_5_version:
.short 5 # DWARF version number
.byte 8 # Address Size
.byte 0 # Segment Selector Size
.long LH_5_header_end-LH_5_params # Length of Prologue
LH_5_params:
.byte 1 # Minimum Instruction Length
.byte 1 # Maximum Operations per Instruction
.byte 1 # Default is_stmt
.byte -5 # Line Base
.byte 14 # Line Range
.byte 13 # Opcode Base
.byte 0 # Standard Opcode Lengths
.byte 1
.byte 1
.byte 1
.byte 1
.byte 0
.byte 0
.byte 0
.byte 1
.byte 0
.byte 0
.byte 1
# Directory table format
.byte 1 # One element per directory entry
.byte 1 # DW_LNCT_path
.byte 0x08 # DW_FORM_string
# Directory table entries
.byte 2 # Two directories
.asciz "Directory5a"
.asciz "Directory5b"
# File table format
.byte 4 # Four elements per file entry
.byte 1 # DW_LNCT_path
.byte 0x08 # DW_FORM_string
.byte 2 # DW_LNCT_directory_index
.byte 0x0b # DW_FORM_data1
.byte 3 # DW_LNCT_timestamp
.byte 0x0f # DW_FORM_udata
.byte 4 # DW_LNCT_size
.byte 0x0f # DW_FORM_udata
# File table entries
.byte 2 # Two files
.asciz "File5a"
.byte 1
.byte 0x51
.byte 0x52
.asciz "File5b"
.byte 2
.byte 0x53
.byte 0x54
LH_5_header_end:
# Line number program, which is empty.
LH_5_end:

View File

@ -7,13 +7,13 @@ CHECK-LABEL: .debug_info contents:
The v4 CU header.
CHECK: 0x00000000: Compile Unit: length = 0x00000011 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000015)
CHECK: 0x00000000: Compile Unit: length = 0x00000015 version = 0x0004 abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000019)
CHECK: 0x0000000b: DW_TAG_compile_unit
The v5 normal CU header.
CHECK: 0x00000015: Compile Unit: length = 0x00000012 version = 0x0005 unit_type = DW_UT_compile abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x0000002b)
CHECK: 0x00000021: DW_TAG_compile_unit
CHECK: 0x00000019: Compile Unit: length = 0x00000016 version = 0x0005 unit_type = DW_UT_compile abbr_offset = 0x0000 addr_size = 0x08 (next unit at 0x00000033)
CHECK: 0x00000025: DW_TAG_compile_unit
CHECK-LABEL: .debug_types contents:
@ -27,3 +27,33 @@ CHECK: .debug_types.dwo contents:
CHECK: 0x00000000: Type Unit: length = 0x00000020 version = 0x0005 unit_type = DW_UT_split_type abbr_offset = 0x0000 addr_size = 0x08 name = 'V5_split_type_unit' type_signature = 0x8899aabbccddeeff type_offset = 0x001d (next unit at 0x00000024)
CHECK: 0x00000018: DW_TAG_type_unit
CHECK-LABEL: .debug_line contents:
The v4 line table header.
CHECK: Line table prologue:
CHECK: version: 4
CHECK-NOT: address_size
CHECK-NOT: seg_select_size
CHECK: max_ops_per_inst: 1
CHECK: include_directories[ 1] = 'Directory4a'
CHECK: include_directories[ 2] = 'Directory4b'
CHECK-NOT: include_directories
CHECK: file_names[ 1] 1 0x00000041 0x00000042 File4a{{$}}
CHECK: file_names[ 2] 0 0x00000043 0x00000044 File4b{{$}}
CHECK-NOT: file_names
The v5 line table header.
CHECK: Line table prologue:
CHECK: version: 5
CHECK: address_size: 8
CHECK: seg_select_size: 0
CHECK: max_ops_per_inst: 1
CHECK: include_directories[ 1] = 'Directory5a'
CHECK: include_directories[ 2] = 'Directory5b'
CHECK-NOT: include_directories
CHECK: file_names[ 1] 1 0x00000051 0x00000052 File5a{{$}}
CHECK: file_names[ 2] 2 0x00000053 0x00000054 File5b{{$}}
CHECK-NOT: file_names