Skip to content

Commit 20948df

Browse files
[DWARFVerifier] Fix debug_str_offsets DWARF version detection (#81303)
The DWARF 5 debug_str_offsets section starts with a header, which must be skipped in order to access the underlying `strp`s. However, the verifier supports some pre-standardization version of this section (with the same section name), which does not have a header. In this case, the offsets start on the first byte of the section. More in [1] and [2] about this legacy section. How does The DWARF verifier figure out which version to use? It manually reads the **first** header in debug_info and uses that. This is wrong when multiple debug_str_offset sections have been linked together, in particular it is wrong in the following two cases: 1. A standard DWARF 4 object file (i.e. no debug_str_offsets) linked with a standard DWARF 5 object file. 2. A non-standard DWARF 4 object file (i.e. containing the header-less debug_str_offsets section) linked with a standard DWARF 5 object file. Based on discussions in #81210, the legacy version is only possible with dwo files, and dwo files cannot mix the legacy version with the dwarf 5 version. As such, we change the verifier to only check the debug_info header in the case of dwo files. If it sees a dwarf 4 version, it handles it the legacy way. Note: the modified test was technically testing an unsupported combination of dwarf version + non-dwo sections. To see why, simply note that the test contained no `debug_info.dwo` sections, so the call to DWARFObject::forEachInfoDWOSections was doing nothing. We were finding the error through the "standard version", which shouldn't happen. [1]: https://gcc.gnu.org/wiki/DebugFission [2]: https://gcc.gnu.org/wiki/DebugFissionDWP
1 parent dfbe2bc commit 20948df

File tree

4 files changed

+94
-31
lines changed

4 files changed

+94
-31
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,9 +360,9 @@ class DWARFVerifier {
360360
///
361361
/// \returns true if the .debug_line verifies successfully, false otherwise.
362362
bool handleDebugStrOffsets();
363-
bool verifyDebugStrOffsets(
364-
StringRef SectionName, const DWARFSection &Section, StringRef StrData,
365-
void (DWARFObject::*)(function_ref<void(const DWARFSection &)>) const);
363+
bool verifyDebugStrOffsets(std::optional<dwarf::DwarfFormat> LegacyFormat,
364+
StringRef SectionName, const DWARFSection &Section,
365+
StringRef StrData);
366366

367367
/// Emits any aggregate information collected, depending on the dump options
368368
void summarize();

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,42 +1880,47 @@ bool DWARFVerifier::handleDebugStrOffsets() {
18801880
OS << "Verifying .debug_str_offsets...\n";
18811881
const DWARFObject &DObj = DCtx.getDWARFObj();
18821882
bool Success = true;
1883+
1884+
// dwo sections may contain the legacy debug_str_offsets format (and they
1885+
// can't be mixed with dwarf 5's format). This section format contains no
1886+
// header.
1887+
// As such, check the version from debug_info and, if we are in the legacy
1888+
// mode (Dwarf <= 4), extract Dwarf32/Dwarf64.
1889+
std::optional<DwarfFormat> DwoLegacyDwarf4Format;
1890+
DObj.forEachInfoDWOSections([&](const DWARFSection &S) {
1891+
if (DwoLegacyDwarf4Format)
1892+
return;
1893+
DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0);
1894+
uint64_t Offset = 0;
1895+
DwarfFormat InfoFormat = DebugInfoData.getInitialLength(&Offset).second;
1896+
if (uint16_t InfoVersion = DebugInfoData.getU16(&Offset); InfoVersion <= 4)
1897+
DwoLegacyDwarf4Format = InfoFormat;
1898+
});
1899+
18831900
Success &= verifyDebugStrOffsets(
1884-
".debug_str_offsets.dwo", DObj.getStrOffsetsDWOSection(),
1885-
DObj.getStrDWOSection(), &DWARFObject::forEachInfoDWOSections);
1901+
DwoLegacyDwarf4Format, ".debug_str_offsets.dwo",
1902+
DObj.getStrOffsetsDWOSection(), DObj.getStrDWOSection());
18861903
Success &= verifyDebugStrOffsets(
1887-
".debug_str_offsets", DObj.getStrOffsetsSection(), DObj.getStrSection(),
1888-
&DWARFObject::forEachInfoSections);
1904+
/*LegacyFormat=*/std::nullopt, ".debug_str_offsets",
1905+
DObj.getStrOffsetsSection(), DObj.getStrSection());
18891906
return Success;
18901907
}
18911908

18921909
bool DWARFVerifier::verifyDebugStrOffsets(
1893-
StringRef SectionName, const DWARFSection &Section, StringRef StrData,
1894-
void (DWARFObject::*VisitInfoSections)(
1895-
function_ref<void(const DWARFSection &)>) const) {
1910+
std::optional<DwarfFormat> LegacyFormat, StringRef SectionName,
1911+
const DWARFSection &Section, StringRef StrData) {
18961912
const DWARFObject &DObj = DCtx.getDWARFObj();
1897-
uint16_t InfoVersion = 0;
1898-
DwarfFormat InfoFormat = DwarfFormat::DWARF32;
1899-
(DObj.*VisitInfoSections)([&](const DWARFSection &S) {
1900-
if (InfoVersion)
1901-
return;
1902-
DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0);
1903-
uint64_t Offset = 0;
1904-
InfoFormat = DebugInfoData.getInitialLength(&Offset).second;
1905-
InfoVersion = DebugInfoData.getU16(&Offset);
1906-
});
19071913

19081914
DWARFDataExtractor DA(DObj, Section, DCtx.isLittleEndian(), 0);
1909-
19101915
DataExtractor::Cursor C(0);
19111916
uint64_t NextUnit = 0;
19121917
bool Success = true;
19131918
while (C.seek(NextUnit), C.tell() < DA.getData().size()) {
19141919
DwarfFormat Format;
19151920
uint64_t Length;
19161921
uint64_t StartOffset = C.tell();
1917-
if (InfoVersion == 4) {
1918-
Format = InfoFormat;
1922+
if (LegacyFormat) {
1923+
Format = *LegacyFormat;
19191924
Length = DA.getData().size();
19201925
NextUnit = C.tell() + Length;
19211926
} else {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# RUN: yaml2obj %s -o %t.o
2+
# RUN: llvm-dwarfdump -debug-str-offsets -verify %t.o | FileCheck %s
3+
4+
# CHECK: Verifying .debug_str_offsets...
5+
# CHECK: No errors
6+
7+
# Check that when mixing standard DWARF 4 debug information with standard DWARF
8+
# 5 debug information, the verifier correctly interprets the debug_str_offsets
9+
# section as a standards-conforming DWARF 5 section.
10+
11+
--- !ELF
12+
FileHeader:
13+
Class: ELFCLASS64
14+
Data: ELFDATA2LSB
15+
Type: ET_EXEC
16+
DWARF:
17+
debug_str:
18+
- 'cu1'
19+
- 'cu2'
20+
debug_str_offsets:
21+
- Offsets:
22+
- 0x0
23+
debug_abbrev:
24+
- Table:
25+
- Code: 0x1
26+
Tag: DW_TAG_compile_unit
27+
Children: DW_CHILDREN_no
28+
Attributes:
29+
- Attribute: DW_AT_name
30+
Form: DW_FORM_strp
31+
- Code: 0x2
32+
Tag: DW_TAG_compile_unit
33+
Children: DW_CHILDREN_no
34+
Attributes:
35+
- Attribute: DW_AT_name
36+
Form: DW_FORM_strx1
37+
- Attribute: DW_AT_str_offsets_base
38+
Form: DW_FORM_sec_offset
39+
debug_info:
40+
- Version: 4
41+
AbbrevTableID: 0
42+
AbbrOffset: 0x0
43+
AddrSize: 8
44+
Entries:
45+
- AbbrCode: 0x1
46+
Values:
47+
- Value: 0x4
48+
- Version: 5
49+
UnitType: DW_UT_compile
50+
AbbrOffset: 0x0
51+
AddrSize: 8
52+
AbbrevTableID: 0
53+
Entries:
54+
- AbbrCode: 0x2
55+
Values:
56+
- Value: 0x0
57+
- Value: 0x8 # str offsets base

llvm/test/tools/llvm-dwarfdump/X86/verify_invalid_str_offsets.yaml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# CHECK-NEXT: error: .debug_str_offsets: contribution 0x29: length exceeds available space (contribution offset (0x29) + length field space (0x4) + length (0x5000000) == 0x500002D > section size 0x30)
1414
# Errors detected.
1515

16-
# V4: error: .debug_str_offsets: contribution 0x0: index 0x2: invalid string offset *0x8 == 0x2, is neither zero nor immediately following a null character
16+
# V4: error: .debug_str_offsets.dwo: contribution 0x0: index 0x2: invalid string offset *0x8 == 0x2, is neither zero nor immediately following a null character
1717

1818

1919
#--- v4.yaml
@@ -23,16 +23,17 @@ FileHeader:
2323
Data: ELFDATA2LSB
2424
Type: ET_EXEC
2525
DWARF:
26-
debug_str:
27-
- 'foo'
28-
- 'bar'
29-
debug_info:
30-
- Version: 4
31-
AddrSize: 4
3226
Sections:
33-
- Name: '.debug_str_offsets'
27+
- Name: '.debug_info.dwo'
28+
Type: SHT_PROGBITS
29+
Content: "0700000004000000000004"
30+
- Name: '.debug_str_offsets.dwo'
3431
Type: SHT_PROGBITS
3532
Content: "000000000400000002000000"
33+
- Name: 'debug_str.dwo'
34+
Type: SHT_PROGBITS
35+
Content: "666F6F0062617200"
36+
3637

3738
#--- v5.yaml
3839
--- !ELF

0 commit comments

Comments
 (0)