145 changes: 133 additions & 12 deletions llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,123 @@ static void dumpAccelSection(raw_ostream &OS, StringRef Name,
Accel.dump(OS);
}

static void
dumpDWARFv5StringOffsetsSection(raw_ostream &OS, StringRef SectionName,
const DWARFSection &StringOffsetsSection,
StringRef StringSection, bool LittleEndian) {
DataExtractor StrOffsetExt(StringOffsetsSection.Data, LittleEndian, 0);
uint32_t Offset = 0;
uint64_t SectionSize = StringOffsetsSection.Data.size();

while (Offset < SectionSize) {
unsigned Version = 0;
DwarfFormat Format = DWARF32;
unsigned EntrySize = 4;
// Perform validation and extract the segment size from the header.
if (!StrOffsetExt.isValidOffsetForDataOfSize(Offset, 4)) {
OS << "error: invalid contribution to string offsets table in section ."
<< SectionName << ".\n";
return;
}
uint32_t ContributionStart = Offset;
uint64_t ContributionSize = StrOffsetExt.getU32(&Offset);
// A contribution size of 0xffffffff indicates DWARF64, with the actual size
// in the following 8 bytes. Otherwise, the DWARF standard mandates that
// the contribution size must be at most 0xfffffff0.
if (ContributionSize == 0xffffffff) {
if (!StrOffsetExt.isValidOffsetForDataOfSize(Offset, 8)) {
OS << "error: invalid contribution to string offsets table in section ."
<< SectionName << ".\n";
return;
}
Format = DWARF64;
EntrySize = 8;
ContributionSize = StrOffsetExt.getU64(&Offset);
} else if (ContributionSize > 0xfffffff0) {
OS << "error: invalid contribution to string offsets table in section ."
<< SectionName << ".\n";
return;
}

// We must ensure that we don't read a partial record at the end, so we
// validate for a multiple of EntrySize. Also, we're expecting a version
// number and padding, which adds an additional 4 bytes.
uint64_t ValidationSize =
4 + ((ContributionSize + EntrySize - 1) & (-(uint64_t)EntrySize));
if (!StrOffsetExt.isValidOffsetForDataOfSize(Offset, ValidationSize)) {
OS << "error: contribution to string offsets table in section ."
<< SectionName << " has invalid length.\n";
return;
}

Version = StrOffsetExt.getU16(&Offset);
Offset += 2;
OS << format("0x%8.8x: ", ContributionStart);
OS << "Contribution size = " << ContributionSize
<< ", Version = " << Version << "\n";

uint32_t ContributionBase = Offset;
DataExtractor StrData(StringSection, LittleEndian, 0);
while (Offset - ContributionBase < ContributionSize) {
OS << format("0x%8.8x: ", Offset);
// FIXME: We can only extract strings in DWARF32 format at the moment.
uint64_t StringOffset = getRelocatedValue(
StrOffsetExt, EntrySize, &Offset, &StringOffsetsSection.Relocs);
if (Format == DWARF32) {
OS << format("%8.8x ", StringOffset);
uint32_t StringOffset32 = (uint32_t)StringOffset;
const char *S = StrData.getCStr(&StringOffset32);
if (S)
OS << format("\"%s\"", S);
} else
OS << format("%16.16x ", StringOffset);
OS << "\n";
}
}
}

// Dump a DWARF string offsets section. This may be a DWARF v5 formatted
// string offsets section, where each compile or type unit contributes a
// number of entries (string offsets), with each contribution preceded by
// a header containing size and version number. Alternatively, it may be a
// monolithic series of string offsets, as generated by the pre-DWARF v5
// implementation of split DWARF.
static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName,
const DWARFSection &StringOffsetsSection,
StringRef StringSection, bool LittleEndian,
unsigned MaxVersion) {
if (StringOffsetsSection.Data.empty())
return;
OS << "\n." << SectionName << " contents:\n";
// If we have at least one (compile or type) unit with DWARF v5 or greater,
// we assume that the section is formatted like a DWARF v5 string offsets
// section.
if (MaxVersion >= 5)
dumpDWARFv5StringOffsetsSection(OS, SectionName, StringOffsetsSection,
StringSection, LittleEndian);
else {
DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0);
uint32_t offset = 0;
uint64_t size = StringOffsetsSection.Data.size();
// Ensure that size is a multiple of the size of an entry.
if (size & ((uint64_t)(sizeof(uint32_t) - 1))) {
OS << "error: size of ." << SectionName << " is not a multiple of "
<< sizeof(uint32_t) << ".\n";
size &= -(uint64_t)sizeof(uint32_t);
}
DataExtractor StrData(StringSection, LittleEndian, 0);
while (offset < size) {
OS << format("0x%8.8x: ", offset);
uint32_t StringOffset = strOffsetExt.getU32(&offset);
OS << format("%8.8x ", StringOffset);
const char *S = StrData.getCStr(&StringOffset);
if (S)
OS << format("\"%s\"", S);
OS << "\n";
}
}
}

void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts){

DIDumpType DumpType = DumpOpts.DumpType;
Expand Down Expand Up @@ -258,17 +375,15 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts){
true /* GnuStyle */)
.dump("debug_gnu_pubtypes", OS);

if ((DumpType == DIDT_All || DumpType == DIDT_StrOffsetsDwo) &&
!getStringOffsetDWOSection().empty()) {
OS << "\n.debug_str_offsets.dwo contents:\n";
DataExtractor strOffsetExt(getStringOffsetDWOSection(), isLittleEndian(),
0);
offset = 0;
uint64_t size = getStringOffsetDWOSection().size();
while (offset < size) {
OS << format("0x%8.8x: ", offset);
OS << format("%8.8x\n", strOffsetExt.getU32(&offset));
}
if (DumpType == DIDT_All || DumpType == DIDT_StrOffsets)
dumpStringOffsetsSection(OS, "debug_str_offsets", getStringOffsetSection(),
getStringSection(), isLittleEndian(),
getMaxVersion());

if (DumpType == DIDT_All || DumpType == DIDT_StrOffsetsDwo) {
dumpStringOffsetsSection(OS, "debug_str_offsets.dwo",
getStringOffsetDWOSection(), getStringDWOSection(),
isLittleEndian(), getMaxVersion());
}

if ((DumpType == DIDT_All || DumpType == DIDT_GdbIndex) &&
Expand Down Expand Up @@ -1109,6 +1224,10 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj,
TypesDWOSections[Section].Data = data;
}

// Map platform specific debug section names to DWARF standard section
// names.
name = Obj.mapDebugSectionName(name);

if (RelocatedSection == Obj.section_end())
continue;

Expand Down Expand Up @@ -1141,6 +1260,7 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj,
.Case("debug_loc", &LocSection.Relocs)
.Case("debug_info.dwo", &InfoDWOSection.Relocs)
.Case("debug_line", &LineSection.Relocs)
.Case("debug_str_offsets", &StringOffsetSection.Relocs)
.Case("debug_ranges", &RangeSection.Relocs)
.Case("debug_addr", &AddrSection.Relocs)
.Case("apple_names", &AppleNamesSection.Relocs)
Expand Down Expand Up @@ -1211,6 +1331,7 @@ StringRef *DWARFContextInMemory::MapSectionToMember(StringRef Name) {
.Case("debug_frame", &DebugFrameSection)
.Case("eh_frame", &EHFrameSection)
.Case("debug_str", &StringSection)
.Case("debug_str_offsets", &StringOffsetSection.Data)
.Case("debug_ranges", &RangeSection.Data)
.Case("debug_macinfo", &MacinfoSection)
.Case("debug_pubnames", &PubNamesSection)
Expand All @@ -1222,7 +1343,7 @@ StringRef *DWARFContextInMemory::MapSectionToMember(StringRef Name) {
.Case("debug_loc.dwo", &LocDWOSection.Data)
.Case("debug_line.dwo", &LineDWOSection.Data)
.Case("debug_str.dwo", &StringDWOSection)
.Case("debug_str_offsets.dwo", &StringOffsetDWOSection)
.Case("debug_str_offsets.dwo", &StringOffsetDWOSection.Data)
.Case("debug_addr", &AddrSection.Data)
.Case("apple_names", &AppleNamesSection.Data)
.Case("apple_types", &AppleTypesSection.Data)
Expand Down
8 changes: 6 additions & 2 deletions llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const {
return (FC == FC_Address);
case DW_FORM_GNU_str_index:
case DW_FORM_GNU_strp_alt:
case DW_FORM_strx:
return (FC == FC_String);
case DW_FORM_implicit_const:
return (FC == FC_Constant);
Expand Down Expand Up @@ -415,6 +416,7 @@ bool DWARFFormValue::extractValue(const DataExtractor &Data,
break;
case DW_FORM_GNU_addr_index:
case DW_FORM_GNU_str_index:
case DW_FORM_strx:
Value.uval = Data.getULEB128(OffsetPtr);
break;
default:
Expand Down Expand Up @@ -542,6 +544,7 @@ void DWARFFormValue::dump(raw_ostream &OS) const {
OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue);
dumpString(OS);
break;
case DW_FORM_strx:
case DW_FORM_GNU_str_index:
OS << format(" indexed (%8.8x) string = ", (uint32_t)UValue);
dumpString(OS);
Expand Down Expand Up @@ -620,10 +623,11 @@ Optional<const char *> DWARFFormValue::getAsCString() const {
if (Form == DW_FORM_GNU_strp_alt || U == nullptr)
return None;
uint32_t Offset = Value.uval;
if (Form == DW_FORM_GNU_str_index) {
uint32_t StrOffset;
if (Form == DW_FORM_GNU_str_index || Form == DW_FORM_strx) {
uint64_t StrOffset;
if (!U->getStringOffsetSectionItem(Offset, StrOffset))
return None;
StrOffset += U->getStringOffsetSectionRelocation(Offset);
Offset = StrOffset;
}
if (const char *Str = U->getStringExtractor().getCStr(&Offset)) {
Expand Down
56 changes: 37 additions & 19 deletions llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ using namespace dwarf;

void DWARFUnitSectionBase::parse(DWARFContext &C, const DWARFSection &Section) {
parseImpl(C, Section, C.getDebugAbbrev(), &C.getRangeSection(),
C.getStringSection(), StringRef(), &C.getAddrSection(),
C.getLineSection().Data, C.isLittleEndian(), false);
C.getStringSection(), C.getStringOffsetSection(),
&C.getAddrSection(), C.getLineSection().Data, C.isLittleEndian(),
false);
}

void DWARFUnitSectionBase::parseDWO(DWARFContext &C,
Expand All @@ -48,19 +49,14 @@ void DWARFUnitSectionBase::parseDWO(DWARFContext &C,

DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section,
const DWARFDebugAbbrev *DA, const DWARFSection *RS,
StringRef SS, StringRef SOS, const DWARFSection *AOS,
StringRef LS, bool LE, bool IsDWO,
StringRef SS, const DWARFSection &SOS,
const DWARFSection *AOS, StringRef LS, bool LE, bool IsDWO,
const DWARFUnitSectionBase &UnitSection,
const DWARFUnitIndex::Entry *IndexEntry)
: Context(DC), InfoSection(Section), Abbrev(DA), RangeSection(RS),
LineSection(LS), StringSection(SS), StringOffsetSection([&]() {
if (IndexEntry)
if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS))
return SOS.slice(C->Offset, C->Offset + C->Length);
return SOS;
}()),
AddrOffsetSection(AOS), isLittleEndian(LE), isDWO(IsDWO),
UnitSection(UnitSection), IndexEntry(IndexEntry) {
LineSection(LS), StringSection(SS), StringOffsetSection(SOS),
StringOffsetSectionBase(0), AddrOffsetSection(AOS), isLittleEndian(LE),
isDWO(IsDWO), UnitSection(UnitSection), IndexEntry(IndexEntry) {
clear();
}

Expand All @@ -77,17 +73,25 @@ bool DWARFUnit::getAddrOffsetSectionItem(uint32_t Index,
}

bool DWARFUnit::getStringOffsetSectionItem(uint32_t Index,
uint32_t &Result) const {
// FIXME: string offset section entries are 8-byte for DWARF64.
const uint32_t ItemSize = 4;
uint32_t Offset = Index * ItemSize;
if (StringOffsetSection.size() < Offset + ItemSize)
uint64_t &Result) const {
unsigned ItemSize = getFormat() == DWARF64 ? 8 : 4;
uint32_t Offset = StringOffsetSectionBase + Index * ItemSize;
if (StringOffsetSection.Data.size() < Offset + ItemSize)
return false;
DataExtractor DA(StringOffsetSection, isLittleEndian, 0);
Result = DA.getU32(&Offset);
DataExtractor DA(StringOffsetSection.Data, isLittleEndian, 0);
Result = ItemSize == 4 ? DA.getU32(&Offset) : DA.getU64(&Offset);
return true;
}

uint64_t DWARFUnit::getStringOffsetSectionRelocation(uint32_t Index) const {
unsigned ItemSize = getFormat() == DWARF64 ? 8 : 4;
uint64_t ByteOffset = StringOffsetSectionBase + Index * ItemSize;
RelocAddrMap::const_iterator AI = getStringOffsetsRelocMap().find(ByteOffset);
if (AI != getStringOffsetsRelocMap().end())
return AI->second.Value;
return 0;
}

bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) {
Length = debug_info.getU32(offset_ptr);
Version = debug_info.getU16(offset_ptr);
Expand Down Expand Up @@ -119,6 +123,9 @@ bool DWARFUnit::extractImpl(DataExtractor debug_info, uint32_t *offset_ptr) {
if (!LengthOK || !VersionOK || !AddrSizeOK)
return false;

// Keep track of the highest DWARF version we encounter across all units.
Context.setMaxVersionIfGreater(Version);

Abbrevs = Abbrev->getAbbreviationDeclarationSet(AbbrOffset);
return Abbrevs != nullptr;
}
Expand Down Expand Up @@ -242,6 +249,17 @@ size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) {
setBaseAddress(*BaseAddr);
AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0);
RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0);

// In general, we derive the offset of the unit's contibution to the
// debug_str_offsets{.dwo} section from the unit DIE's
// DW_AT_str_offsets_base attribute. In dwp files we add to it the offset
// we get from the index table.
StringOffsetSectionBase =
toSectionOffset(UnitDie.find(DW_AT_str_offsets_base), 0);
if (IndexEntry)
if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS))
StringOffsetSectionBase += C->Offset;

// Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for
// skeleton CU DIE, so that DWARF users not aware of it are not broken.
}
Expand Down
16 changes: 14 additions & 2 deletions llvm/lib/MC/MCObjectFileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(const Triple &T) {
DwarfStrSection =
Ctx->getMachOSection("__DWARF", "__debug_str", MachO::S_ATTR_DEBUG,
SectionKind::getMetadata(), "info_string");
DwarfStrOffSection =
Ctx->getMachOSection("__DWARF", "__debug_str_offs", MachO::S_ATTR_DEBUG,
SectionKind::getMetadata(), "section_str_off");
DwarfLocSection =
Ctx->getMachOSection("__DWARF", "__debug_loc", MachO::S_ATTR_DEBUG,
SectionKind::getMetadata(), "section_debug_loc");
Expand Down Expand Up @@ -557,6 +560,11 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T) {
DwarfAccelTypesSection =
Ctx->getELFSection(".apple_types", ELF::SHT_PROGBITS, 0);

// String Offset and Address Sections
DwarfStrOffSection =
Ctx->getELFSection(".debug_str_offsets", DebugSecType, 0);
DwarfAddrSection = Ctx->getELFSection(".debug_addr", DebugSecType, 0);

// Fission Sections
DwarfInfoDWOSection =
Ctx->getELFSection(".debug_info.dwo", DebugSecType, 0);
Expand All @@ -573,7 +581,6 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T) {
Ctx->getELFSection(".debug_loc.dwo", DebugSecType, 0);
DwarfStrOffDWOSection =
Ctx->getELFSection(".debug_str_offsets.dwo", DebugSecType, 0);
DwarfAddrSection = Ctx->getELFSection(".debug_addr", DebugSecType, 0);

// DWP Sections
DwarfCUIndexSection =
Expand Down Expand Up @@ -695,6 +702,11 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ,
SectionKind::getMetadata(), "info_string");
DwarfStrOffSection = Ctx->getCOFFSection(
".debug_str_offsets",
COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ,
SectionKind::getMetadata(), "section_str_off");
DwarfLocSection = Ctx->getCOFFSection(
".debug_loc",
COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
Expand Down Expand Up @@ -749,7 +761,7 @@ void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
".debug_str_offsets.dwo",
COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
COFF::IMAGE_SCN_MEM_READ,
SectionKind::getMetadata());
SectionKind::getMetadata(), "section_str_off_dwo");
DwarfAddrSection = Ctx->getCOFFSection(
".debug_addr",
COFF::IMAGE_SCN_MEM_DISCARDABLE | COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Object/MachOObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4314,3 +4314,9 @@ ObjectFile::createMachOObjectFile(MemoryBufferRef Buffer,
return make_error<GenericBinaryError>("Unrecognized MachO magic number",
object_error::invalid_file_type);
}

StringRef MachOObjectFile::mapDebugSectionName(StringRef Name) const {
return StringSwitch<StringRef>(Name)
.Case("debug_str_offs", "debug_str_offsets")
.Default(Name);
}
277 changes: 277 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-dwp.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
# Test object to verify that dwarfdump handles dwp files with DWARF v5 string
# offset tables. We have 2 CUs and 2 TUs, where it is assumed that
# CU1 and TU1 came from one object file, CU2 and TU2 from a second object
# file.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-dwp.s -filetype=obj \
# -o dwarfdump-str_offsets-dwp.x86_64.o

.section .debug_str.dwo,"MS",@progbits,1
str_producer:
.asciz "Handmade DWARF producer"
str_CU1:
.asciz "Compile_Unit_1"
str_CU1_dir:
.asciz "/home/test/CU1"
str_CU2:
.asciz "Compile_Unit_2"
str_CU2_dir:
.asciz "/home/test/CU2"
str_TU1:
.asciz "Type_Unit_1"
str_TU1_type:
.asciz "MyStruct_1"
str_TU2:
.asciz "Type_Unit_2"
str_TU2_type:
.asciz "MyStruct_2"

.section .debug_str_offsets.dwo,"",@progbits
# Object files 1's portion of the .debug_str_offsets.dwo section.
.debug_str_offsets_object_file1:

# CU1's contribution (from object file 1)
.debug_str_offsets_start_CU1:
.long .debug_str_offsets_end_CU1-.debug_str_offsets_base_CU1
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base_CU1:
.long str_producer-.debug_str.dwo
.long str_CU1-.debug_str.dwo
.long str_CU1_dir-.debug_str.dwo
.debug_str_offsets_end_CU1:

# TU1's contribution (from object file 1)
.debug_str_offsets_start_TU1:
.long .debug_str_offsets_end_TU1-.debug_str_offsets_base_TU1
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base_TU1:
.long str_TU1-.debug_str.dwo
.long str_TU1_type-.debug_str.dwo
.debug_str_offsets_end_TU1:

# Object files 2's portion of the .debug_str_offsets.dwo section.
.debug_str_offsets_object_file2:

# CU2's contribution (from object file 2)
.debug_str_offsets_start_CU2:
.long .debug_str_offsets_end_CU2-.debug_str_offsets_base_CU2
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base_CU2:
.long str_producer-.debug_str.dwo
.long str_CU2-.debug_str.dwo
.long str_CU2_dir-.debug_str.dwo
.debug_str_offsets_end_CU2:

# TU2's contribution (from object file 2)
.debug_str_offsets_start_TU2:
.long .debug_str_offsets_end_TU2-.debug_str_offsets_base_TU2
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base_TU2:
.long str_TU2-.debug_str.dwo
.long str_TU2_type-.debug_str.dwo
.debug_str_offsets_end_TU2:


# Abbrevs are shared for all compile and type units
.section .debug_abbrev.dwo,"",@progbits
.byte 0x01 # Abbrev code
.byte 0x11 # DW_TAG_compile_unit
.byte 0x00 # DW_CHILDREN_no
.byte 0x25 # DW_AT_producer
.byte 0x1a # DW_FORM_strx
.byte 0x03 # DW_AT_name
.byte 0x1a # DW_FORM_strx
.byte 0x72 # DW_AT_str_offsets_base
.byte 0x17 # DW_FORM_sec_offset
.byte 0x03 # DW_AT_name
.byte 0x1a # DW_FORM_strx
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x02 # Abbrev code
.byte 0x41 # DW_TAG_type_unit
.byte 0x01 # DW_CHILDREN_yes
.byte 0x03 # DW_AT_name
.byte 0x1a # DW_FORM_strx
.byte 0x72 # DW_AT_str_offsets_base
.byte 0x17 # DW_FORM_sec_offset
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x03 # Abbrev code
.byte 0x13 # DW_TAG_structure_type
.byte 0x00 # DW_CHILDREN_no (no members)
.byte 0x03 # DW_AT_name
.byte 0x1a # DW_FORM_strx
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x00 # EOM(3)
abbrev_end:

.section .debug_info.dwo,"",@progbits

# DWARF v5 CU header.
CU1_5_start:
.long CU1_5_end-CU1_5_version # Length of Unit
CU1_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev.dwo # Offset Into Abbrev. Section
# The compile-unit DIE, which has a DW_AT_producer, DW_AT_name,
# DW_AT_str_offsets and DW_AT_compdir.
.byte 1 # Abbreviation code
.byte 0 # The index of the producer string
.byte 1 # The index of the CU name string
# The DW_AT_str_offsets_base attribute for CU1 contains the offset of CU1's
# contribution relative to the start of object file 1's portion of the
# .debug_str_offsets section.
.long .debug_str_offsets_base_CU1-.debug_str_offsets_object_file1
.byte 2 # The index of the comp dir string
.byte 0 # NULL
CU1_5_end:

CU2_5_start:
.long CU2_5_end-CU2_5_version # Length of Unit
CU2_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev.dwo # Offset Into Abbrev. Section
# The compile-unit DIE, which has a DW_AT_producer, DW_AT_name,
# DW_AT_str_offsets and DW_AT_compdir.
.byte 1 # Abbreviation code
.byte 0 # The index of the producer string
.byte 1 # The index of the CU name string
# The DW_AT_str_offsets_base attribute for CU2 contains the offset of CU2's
# contribution relative to the start of object file 2's portion of the
# .debug_str_offsets section.
.long .debug_str_offsets_base_CU2-.debug_str_offsets_object_file2
.byte 2 # The index of the comp dir string
.byte 0 # NULL
CU2_5_end:

.section .debug_types.dwo,"",@progbits
# DWARF v5 Type unit header.
TU1_5_start:
.long TU1_5_end-TU1_5_version # Length of Unit
TU1_5_version:
.short 5 # DWARF version number
.byte 2 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev.dwo # Offset Into Abbrev. Section
.quad 0x0011223344556677 # Type Signature
.long TU1_5_type-TU1_5_start # Type offset
# The type-unit DIE, which has a name.
.byte 2 # Abbreviation code
.byte 0 # Index of the unit type name string
# The DW_AT_str_offsets_base attribute for TU1 contains the offset of TU1's
# contribution relative to the start of object file 1's portion of the
# .debug_str_offsets section.
.long .debug_str_offsets_base_TU1-.debug_str_offsets_object_file1
# The type DIE, which has a name.
TU1_5_type:
.byte 3 # Abbreviation code
.byte 1 # Index of the type name string
.byte 0 # NULL
.byte 0 # NULL
TU1_5_end:

TU2_5_start:
.long TU2_5_end-TU2_5_version # Length of Unit
TU2_5_version:
.short 5 # DWARF version number
.byte 2 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev.dwo # Offset Into Abbrev. Section
.quad 0x00aabbccddeeff99 # Type Signature
.long TU2_5_type-TU2_5_start # Type offset
# The type-unit DIE, which has a name.
.byte 2 # Abbreviation code
.byte 0 # Index of the unit type name string
# The DW_AT_str_offsets_base attribute for TU2 contains the offset of TU2's
# contribution relative to the start of object file 2's portion of the
# .debug_str_offsets section.
.long .debug_str_offsets_base_TU2-.debug_str_offsets_object_file2
# The type DIE, which has a name.
TU2_5_type:
.byte 3 # Abbreviation code
.byte 1 # Index of the type name string
.byte 0 # NULL
.byte 0 # NULL
TU2_5_end:

.section .debug_cu_index,"",@progbits
# The index header
.long 2 # Version
.long 3 # Columns of contribution matrix
.long 2 # number of units
.long 2 # number of hash buckets in table

# The signatures for both CUs.
.quad 0xddeeaaddbbaabbee # signature 1
.quad 0xff00ffeeffaaff00 # signature 2
# The indexes for both CUs.
.long 1 # index 1
.long 2 # index 2
# The sections to which both CUs contribute.
.long 1 # DW_SECT_INFO
.long 3 # DW_SECT_ABBREV
.long 6 # DW_SECT_STR_OFFSETS

# The starting offsets of both CU's contributions to info,
# abbrev and string offsets table.
.long CU1_5_start-.debug_info.dwo
.long 0
.long .debug_str_offsets_object_file1-.debug_str_offsets.dwo
.long CU2_5_start-.debug_info.dwo
.long 0
.long .debug_str_offsets_object_file2-.debug_str_offsets.dwo

# The lengths of both CU's contributions to info, abbrev and
# string offsets table.
.long CU1_5_end-CU1_5_start
.long abbrev_end-.debug_abbrev.dwo
.long .debug_str_offsets_end_CU1-.debug_str_offsets_start_CU1
.long CU2_5_end-CU2_5_start
.long abbrev_end-.debug_abbrev.dwo
.long .debug_str_offsets_end_CU2-.debug_str_offsets_start_CU2

.section .debug_tu_index,"",@progbits
# The index header
.long 2 # Version
.long 3 # Columns of contribution matrix
.long 2 # number of units
.long 2 # number of hash buckets in table

# The signatures for both TUs.
.quad 0xeeaaddbbaabbeedd # signature 1
.quad 0x00ffeeffaaff00ff # signature 2
# The indexes for both TUs.
.long 1 # index 1
.long 2 # index 2
# The sections to which both TUs contribute.
.long 2 # DW_SECT_TYPES
.long 3 # DW_SECT_ABBREV
.long 6 # DW_SECT_STR_OFFSETS

# The starting offsets of both TU's contributions to info,
# abbrev and string offsets table.
.long TU1_5_start-.debug_types.dwo
.long 0
.long .debug_str_offsets_object_file1-.debug_str_offsets.dwo
.long TU2_5_start-.debug_types.dwo
.long 0
.long .debug_str_offsets_object_file2-.debug_str_offsets.dwo

# The lengths of both TU's contributions to info, abbrev and
# string offsets table.
.long TU1_5_end-TU1_5_start
.long abbrev_end-.debug_abbrev.dwo
.long .debug_str_offsets_end_TU1-.debug_str_offsets_start_TU1
.long TU2_5_end-TU2_5_start
.long abbrev_end-.debug_abbrev.dwo
.long .debug_str_offsets_end_TU2-.debug_str_offsets_start_TU2
34 changes: 34 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-invalid-1.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Test object to verify that llvm-dwarfdump handles an invalid string offsets
# table.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-invalid-1.s -filetype=obj \
# -o dwarfdump-str-offsets-invalid-1.x86_64.o
#
# A rudimentary abbrev section.
.section .debug_abbrev,"",@progbits
.byte 0x01 # Abbrev code
.byte 0x11 # DW_TAG_compile_unit
.byte 0x00 # DW_CHILDREN_no
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x00 # EOM(3)

# A rudimentary compile unit to convince dwarfdump that we are dealing with a
# DWARF v5 string offsets table.
.section .debug_info,"",@progbits

# DWARF v5 CU header.
.long CU1_5_end-CU1_5_version # Length of Unit
CU1_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
# A compile-unit DIE, which has no attributes.
.byte 1 # Abbreviation code
CU1_5_end:

.section .debug_str_offsets,"",@progbits
# A degenerate section, not enough for a single contribution size.
.byte 2
Binary file not shown.
36 changes: 36 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-invalid-2.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Test object to verify that llvm-dwarfdump handles an invalid string offsets
# table.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-invalid-2.s -filetype=obj \
# -o dwarfdump-str-offsets-invalid-2.x86_64.o

# A rudimentary abbrev section.
.section .debug_abbrev,"",@progbits
.byte 0x01 # Abbrev code
.byte 0x11 # DW_TAG_compile_unit
.byte 0x00 # DW_CHILDREN_no
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x00 # EOM(3)

# A rudimentary compile unit to convince dwarfdump that we are dealing with a
# DWARF v5 string offsets table.
.section .debug_info,"",@progbits

# DWARF v5 CU header.
.long CU1_5_end-CU1_5_version # Length of Unit
CU1_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
# A compile-unit DIE, which has no attributes.
.byte 1 # Abbreviation code
CU1_5_end:

.section .debug_str_offsets,"",@progbits
# A degenerate section with fewer bytes than required for a DWARF64 size.
.long 0xffffffff
.long 0
.short 4
Binary file not shown.
88 changes: 88 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-invalid-3.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Test object to verify that llvm-dwarfdump handles an invalid string offsets
# table.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-invalid-3.s -filetype=obj \
# -o dwarfdump-str-offsets-invalid-3.x86_64.o

.section .debug_str,"MS",@progbits,1
str_producer:
.asciz "Handmade DWARF producer"
str_CU1:
.asciz "Compile_Unit_1"
str_CU1_dir:
.asciz "/home/test/CU1"
str_CU2:
.asciz "Compile_Unit_2"
str_CU2_dir:
.asciz "/home/test/CU2"
str_TU:
.asciz "Type_Unit"
str_TU_type:
.asciz "MyStruct"

.section .debug_str.dwo,"MS",@progbits,1
dwo_str_CU_5_producer:
.asciz "Handmade split DWARF producer"
dwo_str_CU_5_name:
.asciz "V5_split_compile_unit"
dwo_str_CU_5_comp_dir:
.asciz "/home/test/splitCU"
dwo_str_TU_5:
.asciz "V5_split_type_unit"
dwo_str_TU_5_type:
.asciz "V5_split_Mystruct"

# A rudimentary abbrev section.
.section .debug_abbrev,"",@progbits
.byte 0x01 # Abbrev code
.byte 0x11 # DW_TAG_compile_unit
.byte 0x00 # DW_CHILDREN_no
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x00 # EOM(3)

# A rudimentary compile unit to convince dwarfdump that we are dealing with a
# DWARF v5 string offsets table.
.section .debug_info,"",@progbits

# DWARF v5 CU header.
.long CU1_5_end-CU1_5_version # Length of Unit
CU1_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
# A compile-unit DIE, which has no attributes.
.byte 1 # Abbreviation code
CU1_5_end:

.section .debug_str_offsets,"",@progbits
# CU1's contribution
# Invalid length
.long 0xfffffffe
.long .debug_str_offsets_segment0_end-.debug_str_offsets_base0
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base0:
.long str_producer
.long str_CU1
.long str_CU1_dir
.debug_str_offsets_segment0_end:
# CU2's contribution
.long .debug_str_offsets_segment1_end-.debug_str_offsets_base1
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base1:
.long str_producer
.long str_CU2
.long str_CU2_dir
.debug_str_offsets_segment1_end:
# The TU's contribution
.long .debug_str_offsets_segment2_end-.debug_str_offsets_base2
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base2:
.long str_TU
.long str_TU_type
.debug_str_offsets_segment2_end:
Binary file not shown.
50 changes: 50 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-invalid-4.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Test object to verify that llvm-dwarfdump handles an invalid string offsets
# table.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-invalid-4.s -filetype=obj \
# -o dwarfdump-str-offsets-invalid-4.x86_64.o

.section .debug_str,"MS",@progbits,1
str_producer:
.asciz "Handmade DWARF producer"
str_CU1:
.asciz "Compile_Unit_1"

# A rudimentary abbrev section.
.section .debug_abbrev,"",@progbits
.byte 0x01 # Abbrev code
.byte 0x11 # DW_TAG_compile_unit
.byte 0x00 # DW_CHILDREN_no
.byte 0x00 # EOM(1)
.byte 0x00 # EOM(2)
.byte 0x00 # EOM(3)

# A rudimentary compile unit to convince dwarfdump that we are dealing with a
# DWARF v5 string offsets table.
.section .debug_info,"",@progbits

# DWARF v5 CU header.
.long CU1_5_end-CU1_5_version # Length of Unit
CU1_5_version:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
# A compile-unit DIE, which has no attributes.
.byte 1 # Abbreviation code
CU1_5_end:

# Every unit contributes to the string_offsets table.
.section .debug_str_offsets,"",@progbits
# CU1's contribution
# The length is not a multiple of 4. Check that we don't read off the
# end.
.long .debug_str_offsets_segment0_end-.debug_str_offsets_base0
.short 5 # DWARF version
.short 0 # Padding
.debug_str_offsets_base0:
.long str_producer
.long str_CU1
.byte 0
.debug_str_offsets_segment0_end:
Binary file not shown.
10 changes: 10 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets-invalid-5.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Test object to verify that llvm-dwarfdump handles a degenerate string offsets
# section.
#
# To generate the test object:
# llvm-mc -triple x86_64-unknown-linux dwarfdump-str-offsets-invalid-5.s -filetype=obj \
# -o dwarfdump-str-offsets-invalid-5.x86_64.o
# Every unit contributes to the string_offsets table.
.section .debug_str_offsets,"",@progbits
# A degenerate section, not enough for a single entry.
.byte 2
Binary file not shown.
500 changes: 500 additions & 0 deletions llvm/test/DebugInfo/Inputs/dwarfdump-str-offsets.s

Large diffs are not rendered by default.

Binary file not shown.
24 changes: 24 additions & 0 deletions llvm/test/DebugInfo/dwarfdump-str-offsets-invalid.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
; Verify that llvm-dwarfdump handles invalid string offset tables.

RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets-invalid-1.x86_64.o | \
RUN: FileCheck --check-prefix=INVALIDCONTRIB %s
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets-invalid-2.x86_64.o | \
RUN: FileCheck --check-prefix=INVALIDCONTRIB %s
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets-invalid-3.x86_64.o | \
RUN: FileCheck --check-prefix=INVALIDCONTRIB %s
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets-invalid-4.x86_64.o | \
RUN: FileCheck --check-prefix=INVALIDLENGTH %s
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets-invalid-5.x86_64.o | \
RUN: FileCheck --check-prefix=INVALIDSECTIONLENGTH %s

INVALIDCONTRIB: .debug_str_offsets contents:
INVALIDCONTRIB-NOT: contents:
INVALIDCONTRIB: error: invalid contribution to string offsets table in section .debug_str_offsets.

INVALIDLENGTH: .debug_str_offsets contents:
INVALIDLENGTH-NOT: contents:
INVALIDLENGTH: error: contribution to string offsets table in section .debug_str_offsets has invalid length.

INVALIDSECTIONLENGTH: .debug_str_offsets contents:
INVALIDSECTIONLENGTH-NOT: contents:
INVALIDSECTIONLENGTH: error: size of .debug_str_offsets is not a multiple of 4.
76 changes: 76 additions & 0 deletions llvm/test/DebugInfo/dwarfdump-str-offsets.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-str-offsets.x86_64.o | FileCheck %s

; We are using a hand-constructed object file and are interest in the correct
; diplay of the DW_str_offsetsbase attribute, the correct display of strings
; and the dump of the .debug_str_offsets[.dwo] table.
;
; Abbreviation for DW_AT_str_offsets_base
CHECK: .debug_abbrev contents:
CHECK-NOT: contents:
CHECK: DW_TAG_compile_unit
CHECK-NOT: DW_TAG
CHECK: DW_AT_str_offsets_base DW_FORM_sec_offset

; Verify that strings are displayed correctly as indexed strings
CHECK: .debug_info contents:
CHECK-NOT: contents:
CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer [DW_FORM_strx] ( indexed (00000000) string = "Handmade DWARF producer")
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000001) string = "Compile_Unit_1")
CHECK-NEXT: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strx] ( indexed (00000002) string = "/home/test/CU1")

; Second compile unit (b.cpp)
CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer [DW_FORM_strx] ( indexed (00000000) string = "Handmade DWARF producer")
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000001) string = "Compile_Unit_2")
CHECK-NEXT: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x0000001c)
CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strx] ( indexed (00000002) string = "/home/test/CU2")

; The split CU
CHECK: .debug_info.dwo contents:
CHECK-NOT: contents:
CHECK: DW_TAG_compile_unit
CHECK-NEXT: DW_AT_producer [DW_FORM_strx] ( indexed (00000000) string = "Handmade split DWARF producer")
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000001) string = "V5_split_compile_unit")
CHECK-NEXT: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000008)
CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strx] ( indexed (00000002) string = "/home/test/splitCU")

; The type unit
CHECK: .debug_types contents:
CHECK: DW_TAG_type_unit
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000000) string = "Type_Unit")
CHECK-NEXT: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x00000030)
CHECK: DW_TAG_structure_type
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000001) string = "MyStruct")

; The split type unit
CHECK: .debug_types.dwo contents:
CHECK: DW_TAG_type_unit
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000000) string = "V5_split_type_unit")
CHECK-NEXT: DW_AT_str_offsets_base [DW_FORM_sec_offset] (0x0000001c)
CHECK: DW_TAG_structure_type
CHECK-NEXT: DW_AT_name [DW_FORM_strx] ( indexed (00000001) string = "V5_split_Mystruct")

; The .debug_str_offsets section
CHECK: .debug_str_offsets contents:
CHECK-NEXT: 0x00000000: Contribution size = 12, Version = 5
CHECK-NEXT: 0x00000008: 00000000 "Handmade DWARF producer"
CHECK-NEXT: 0x0000000c: 00000018 "Compile_Unit_1"
CHECK-NEXT: 0x00000010: 00000027 "/home/test/CU1"
CHECK-NEXT: 0x00000014: Contribution size = 12, Version = 5
CHECK-NEXT: 0x0000001c: 00000000 "Handmade DWARF producer"
CHECK-NEXT: 0x00000020: 00000036 "Compile_Unit_2"
CHECK-NEXT: 0x00000024: 00000045 "/home/test/CU2"
CHECK-NEXT: 0x00000028: Contribution size = 8, Version = 5
CHECK-NEXT: 0x00000030: 00000054 "Type_Unit"
CHECK-NEXT: 0x00000034: 0000005e "MyStruct"

CHECK: .debug_str_offsets.dwo contents:
CHECK-NEXT: 0x00000000: Contribution size = 12, Version = 5
CHECK-NEXT: 0x00000008: 00000000 "Handmade split DWARF producer"
CHECK-NEXT: 0x0000000c: 0000001e "V5_split_compile_unit"
CHECK-NEXT: 0x00000010: 00000034 "/home/test/splitCU"
CHECK-NEXT: 0x00000014: Contribution size = 8, Version = 5
CHECK-NEXT: 0x0000001c: 00000047 "V5_split_type_unit"
CHECK-NEXT: 0x00000020: 0000005a "V5_split_Mystruct"
1 change: 1 addition & 0 deletions llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ static cl::opt<DIDumpType> DumpType(
clEnumValN(DIDT_GnuPubnames, "gnu_pubnames", ".debug_gnu_pubnames"),
clEnumValN(DIDT_GnuPubtypes, "gnu_pubtypes", ".debug_gnu_pubtypes"),
clEnumValN(DIDT_Str, "str", ".debug_str"),
clEnumValN(DIDT_StrOffsets, "str_offsets", ".debug_str_offsets"),
clEnumValN(DIDT_StrDwo, "str.dwo", ".debug_str.dwo"),
clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo",
".debug_str_offsets.dwo"),
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/obj2yaml/dwarf2yaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ void dumpDebugInfo(DWARFContextInMemory &DCtx, DWARFYAML::Data &Y) {
case dwarf::DW_FORM_line_strp:
case dwarf::DW_FORM_strp_sup:
case dwarf::DW_FORM_GNU_str_index:
case dwarf::DW_FORM_strx:
if (auto Val = FormValue.getValue().getAsCStringOffset())
NewValue.Value = Val.getValue();
break;
Expand Down