Skip to content

Commit 92f2d02

Browse files
committed
DebugInfo: Sink string form validation down from verifier to form parsing
Avoid duplicating the string decoding - improve the error messages down in form parsing (& produce an Expected<const char*> instead of Optional<const char*> to communicate the extra error details)
1 parent 3ce1e94 commit 92f2d02

File tree

8 files changed

+59
-80
lines changed

8 files changed

+59
-80
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class DWARFFormValue {
112112
Optional<UnitOffset> getAsRelativeReference() const;
113113
Optional<uint64_t> getAsUnsignedConstant() const;
114114
Optional<int64_t> getAsSignedConstant() const;
115-
Optional<const char *> getAsCString() const;
115+
Expected<const char *> getAsCString() const;
116116
Optional<uint64_t> getAsAddress() const;
117117
Optional<object::SectionedAddress> getAsSectionedAddress() const;
118118
Optional<uint64_t> getAsSectionOffset() const;
@@ -173,9 +173,14 @@ namespace dwarf {
173173
/// \returns an optional value that contains a value if the form value
174174
/// was valid and was a string.
175175
inline Optional<const char *> toString(const Optional<DWARFFormValue> &V) {
176-
if (V)
177-
return V->getAsCString();
178-
return None;
176+
if (!V)
177+
return None;
178+
Expected<const char*> E = V->getAsCString();
179+
if (!E) {
180+
consumeError(E.takeError());
181+
return None;
182+
}
183+
return *E;
179184
}
180185

181186
/// Take an optional DWARFFormValue and try to extract a string value from it.
@@ -185,10 +190,16 @@ inline Optional<const char *> toString(const Optional<DWARFFormValue> &V) {
185190
/// was valid and was a string.
186191
inline StringRef toStringRef(const Optional<DWARFFormValue> &V,
187192
StringRef Default = {}) {
188-
if (V)
189-
if (auto S = V->getAsCString())
190-
return *S;
191-
return Default;
193+
if (!V)
194+
return Default;
195+
auto S = V->getAsCString();
196+
if (!S) {
197+
consumeError(S.takeError());
198+
return Default;
199+
}
200+
if (!*S)
201+
return Default;
202+
return *S;
192203
}
193204

194205
/// Take an optional DWARFFormValue and extract a string value from it.

llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ class DWARFUnit {
331331

332332
Optional<object::SectionedAddress>
333333
getAddrOffsetSectionItem(uint32_t Index) const;
334-
Optional<uint64_t> getStringOffsetSectionItem(uint32_t Index) const;
334+
Expected<uint64_t> getStringOffsetSectionItem(uint32_t Index) const;
335335

336336
DWARFDataExtractor getDebugInfoExtractor() const;
337337

llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,11 @@ Error DWARFDebugMacro::parseImpl(
194194
if (MacroContributionOffset == MacroToUnits.end())
195195
return createStringError(errc::invalid_argument,
196196
"Macro contribution of the unit not found");
197-
Optional<uint64_t> StrOffset =
197+
Expected<uint64_t> StrOffset =
198198
MacroContributionOffset->second->getStringOffsetSectionItem(
199199
Data.getULEB128(&Offset));
200200
if (!StrOffset)
201-
return createStringError(
202-
errc::invalid_argument,
203-
"String offsets contribution of the unit not found");
201+
return StrOffset.takeError();
204202
E.MacroStr =
205203
MacroContributionOffset->second->getStringExtractor().getCStr(
206204
&*StrOffset);

llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -613,50 +613,53 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
613613
}
614614

615615
void DWARFFormValue::dumpString(raw_ostream &OS) const {
616-
Optional<const char *> DbgStr = getAsCString();
617-
if (DbgStr.hasValue()) {
616+
if (auto DbgStr = dwarf::toString(*this)) {
618617
auto COS = WithColor(OS, HighlightColor::String);
619618
COS.get() << '"';
620-
COS.get().write_escaped(DbgStr.getValue());
619+
COS.get().write_escaped(*DbgStr);
621620
COS.get() << '"';
622621
}
623622
}
624623

625-
Optional<const char *> DWARFFormValue::getAsCString() const {
624+
Expected<const char *> DWARFFormValue::getAsCString() const {
626625
if (!isFormClass(FC_String))
627-
return None;
626+
return make_error<StringError>("Invalid form for string attribute",
627+
inconvertibleErrorCode());
628628
if (Form == DW_FORM_string)
629629
return Value.cstr;
630630
// FIXME: Add support for DW_FORM_GNU_strp_alt
631631
if (Form == DW_FORM_GNU_strp_alt || C == nullptr)
632-
return None;
632+
return make_error<StringError>("Unsupported form for string attribute",
633+
inconvertibleErrorCode());
633634
uint64_t Offset = Value.uval;
634-
if (Form == DW_FORM_line_strp) {
635-
// .debug_line_str is tracked in the Context.
636-
if (const char *Str = C->getLineStringExtractor().getCStr(&Offset))
637-
return Str;
638-
return None;
639-
}
635+
Optional<uint32_t> Index;
640636
if (Form == DW_FORM_GNU_str_index || Form == DW_FORM_strx ||
641637
Form == DW_FORM_strx1 || Form == DW_FORM_strx2 || Form == DW_FORM_strx3 ||
642638
Form == DW_FORM_strx4) {
643639
if (!U)
644-
return None;
645-
Optional<uint64_t> StrOffset = U->getStringOffsetSectionItem(Offset);
640+
return make_error<StringError>("API limitation - string extraction not "
641+
"available without a DWARFUnit",
642+
inconvertibleErrorCode());
643+
Expected<uint64_t> StrOffset = U->getStringOffsetSectionItem(Offset);
644+
Index = Offset;
646645
if (!StrOffset)
647-
return None;
646+
return StrOffset.takeError();
648647
Offset = *StrOffset;
649648
}
650649
// Prefer the Unit's string extractor, because for .dwo it will point to
651650
// .debug_str.dwo, while the Context's extractor always uses .debug_str.
652-
if (U) {
653-
if (const char *Str = U->getStringExtractor().getCStr(&Offset))
654-
return Str;
655-
return None;
656-
}
657-
if (const char *Str = C->getStringExtractor().getCStr(&Offset))
651+
DataExtractor StrData = Form == DW_FORM_line_strp
652+
? C->getLineStringExtractor()
653+
: U ? U->getStringExtractor()
654+
: C->getStringExtractor();
655+
if (const char *Str = StrData.getCStr(&Offset))
658656
return Str;
659-
return None;
657+
std::string Msg = FormEncodingString(Form).str();
658+
if (Index)
659+
Msg += (" uses index " + Twine(*Index) + ", but the referenced string").str();
660+
Msg += (" offset " + Twine(Offset) + " is beyond .debug_str bounds").str();
661+
return make_error<StringError>(Msg,
662+
inconvertibleErrorCode());
660663
}
661664

662665
Optional<uint64_t> DWARFFormValue::getAsAddress() const {

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,17 @@ DWARFUnit::getAddrOffsetSectionItem(uint32_t Index) const {
214214
return {{Address, Section}};
215215
}
216216

217-
Optional<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const {
217+
Expected<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const {
218218
if (!StringOffsetsTableContribution)
219-
return None;
219+
return make_error<StringError>(
220+
"DW_FORM_strx used without a valid string offsets table",
221+
inconvertibleErrorCode());
220222
unsigned ItemSize = getDwarfStringOffsetsByteSize();
221223
uint64_t Offset = getStringOffsetsBase() + Index * ItemSize;
222224
if (StringOffsetSection.Data.size() < Offset + ItemSize)
223-
return None;
225+
return make_error<StringError>("DW_FORM_strx uses index " + Twine(Index) +
226+
", which is too large",
227+
inconvertibleErrorCode());
224228
DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection,
225229
isLittleEndian, 0);
226230
return DA.getRelocatedValue(ItemSize, &Offset);

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,6 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
616616
DWARFAttribute &AttrValue,
617617
ReferenceMap &LocalReferences,
618618
ReferenceMap &CrossUnitReferences) {
619-
const DWARFObject &DObj = DCtx.getDWARFObj();
620619
auto DieCU = Die.getDwarfUnit();
621620
unsigned NumErrors = 0;
622621
const auto Form = AttrValue.Value.getForm();
@@ -667,51 +666,15 @@ unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die,
667666
}
668667
break;
669668
}
670-
case DW_FORM_strp: {
671-
auto SecOffset = AttrValue.Value.getAsSectionOffset();
672-
assert(SecOffset); // DW_FORM_strp is a section offset.
673-
if (SecOffset && *SecOffset >= DObj.getStrSection().size()) {
674-
++NumErrors;
675-
error() << "DW_FORM_strp offset beyond .debug_str bounds:\n";
676-
dump(Die) << '\n';
677-
}
678-
break;
679-
}
669+
case DW_FORM_strp:
680670
case DW_FORM_strx:
681671
case DW_FORM_strx1:
682672
case DW_FORM_strx2:
683673
case DW_FORM_strx3:
684674
case DW_FORM_strx4: {
685-
auto Index = AttrValue.Value.getRawUValue();
686-
auto DieCU = Die.getDwarfUnit();
687-
// Check that we have a valid DWARF v5 string offsets table.
688-
if (!DieCU->getStringOffsetsTableContribution()) {
689-
++NumErrors;
690-
error() << FormEncodingString(Form)
691-
<< " used without a valid string offsets table:\n";
692-
dump(Die) << '\n';
693-
break;
694-
}
695-
// Check that the index is within the bounds of the section.
696-
unsigned ItemSize = DieCU->getDwarfStringOffsetsByteSize();
697-
// Use a 64-bit type to calculate the offset to guard against overflow.
698-
uint64_t Offset =
699-
(uint64_t)DieCU->getStringOffsetsBase() + Index * ItemSize;
700-
if (DObj.getStrOffsetsSection().Data.size() < Offset + ItemSize) {
701-
++NumErrors;
702-
error() << FormEncodingString(Form) << " uses index "
703-
<< format("%" PRIu64, Index) << ", which is too large:\n";
704-
dump(Die) << '\n';
705-
break;
706-
}
707-
// Check that the string offset is valid.
708-
uint64_t StringOffset = *DieCU->getStringOffsetSectionItem(Index);
709-
if (StringOffset >= DObj.getStrSection().size()) {
675+
if (Error E = AttrValue.Value.getAsCString().takeError()) {
710676
++NumErrors;
711-
error() << FormEncodingString(Form) << " uses index "
712-
<< format("%" PRIu64, Index)
713-
<< ", but the referenced string"
714-
" offset is beyond .debug_str bounds:\n";
677+
error() << toString(std::move(E)) << ":\n";
715678
dump(Die) << '\n';
716679
}
717680
break;

llvm/test/DebugInfo/X86/debug-macro-empty-str-offset.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# RUN: llvm-mc -triple x86_64-unknown-linux -filetype=obj %s -o -| \
66
# RUN: not llvm-dwarfdump -debug-macro - /dev/null 2>&1 | FileCheck %s
77

8-
# CHECK: error: String offsets contribution of the unit not found
8+
# CHECK: error: DW_FORM_strx used without a valid string offsets table
99

1010
.section .debug_abbrev,"",@progbits
1111
.byte 1 # Abbreviation Code

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# RUN: not llvm-dwarfdump -debug-info -verify %t.o | FileCheck %s
33

44
# CHECK: Verifying non-dwo Units...
5-
# CHECK-NEXT: error: DW_FORM_strp offset beyond .debug_str bounds:
5+
# CHECK-NEXT: error: DW_FORM_strp offset 4660 is beyond .debug_str bounds:
66

77
--- !ELF
88
FileHeader:

0 commit comments

Comments
 (0)