Skip to content

Commit 9911af4

Browse files
committed
WIP: Verify -gsimple-template-names=mangled values
Clang will encode names that should be able to be simplified as "_STNname|<template, args>" (eg: "_STNt1|<int>") - this verification mode will detect these names, decode them, create the original name ("t1<int>") and the simple name ("t1") - letting the simple name run through the usual rebuilding logic - then compare the two sources of the full name - the rebuilt and the _STN encoding. This helps ensure that -gsimple-template-names is lossless.
1 parent 62d6ff5 commit 9911af4

File tree

5 files changed

+990
-239
lines changed

5 files changed

+990
-239
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ class DWARFDie {
236236
/// for ShortName if LinkageName is not found.
237237
/// Returns null if no name is found.
238238
const char *getName(DINameKind Kind) const;
239+
void getFullName(raw_string_ostream &,
240+
std::string *OriginalFullName = nullptr) const;
239241

240242
/// Return the DIE short name resolving DW_AT_specification or
241243
/// DW_AT_abstract_origin references if necessary. Returns null if no name

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class DWARFVerifier {
125125
bool verifyUnitHeader(const DWARFDataExtractor DebugInfoData,
126126
uint64_t *Offset, unsigned UnitIndex, uint8_t &UnitType,
127127
bool &isUnitDWARF64);
128+
bool verifyName(const DWARFDie &Die);
128129

129130
/// Verifies the header of a unit in a .debug_info or .debug_types section.
130131
///

llvm/lib/DebugInfo/DWARF/DWARFDie.cpp

Lines changed: 230 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ struct DWARFTypePrinter {
175175
OS << ")]";
176176
}
177177
}
178+
EndedWithTemplate = false;
178179
}
179180

180181
DWARFDie skipQualifiers(DWARFDie D) {
@@ -197,9 +198,12 @@ struct DWARFTypePrinter {
197198
OS << '(';
198199
OS << Ptr;
199200
Word = false;
201+
EndedWithTemplate = false;
200202
}
201203

202-
DWARFDie appendUnqualifiedNameBefore(DWARFDie D) {
204+
DWARFDie
205+
appendUnqualifiedNameBefore(DWARFDie D,
206+
std::string *OriginalFullName = nullptr) {
203207
Word = true;
204208
if (!D) {
205209
OS << "void";
@@ -268,15 +272,54 @@ struct DWARFTypePrinter {
268272
EndedWithTemplate = false;
269273
break;
270274
}
271-
default:
275+
/*
276+
case DW_TAG_structure_type:
277+
case DW_TAG_class_type:
278+
case DW_TAG_enumeration_type:
279+
case DW_TAG_base_type:
280+
*/
281+
default: {
272282
const char *NamePtr = dwarf::toString(D.find(DW_AT_name), nullptr);
273283
if (!NamePtr) {
274-
appendTypeTagName(D.getTag());
275-
break;
284+
StringRef TagStr = TagString(D.getTag());
285+
static constexpr StringRef Prefix = "DW_TAG_";
286+
static constexpr StringRef Suffix = "_type";
287+
if (TagStr.startswith(Prefix) && TagStr.endswith(Suffix))
288+
OS << TagStr.substr(Prefix.size(),
289+
TagStr.size() - (Prefix.size() + Suffix.size()))
290+
<< " ";
291+
return Inner;
292+
}
293+
Word = true;
294+
StringRef Name = NamePtr;
295+
static constexpr StringRef MangledPrefix = "_STN";
296+
if (Name.startswith(MangledPrefix)) {
297+
Name = Name.drop_front(MangledPrefix.size());
298+
auto Separator = Name.find('|');
299+
assert(Separator != StringRef::npos);
300+
StringRef BaseName = Name.substr(0, Separator);
301+
StringRef TemplateArgs = Name.substr(Separator + 1);
302+
if (OriginalFullName)
303+
*OriginalFullName = (BaseName + TemplateArgs).str();
304+
Name = BaseName;
305+
} else
306+
EndedWithTemplate = Name.endswith(">");
307+
OS << Name;
308+
// FIXME: This needs to be a bit more narrow, it would fail to
309+
// reconstitute a non-operator overload that is a template, like
310+
// "operator_thing<int>"
311+
if (!Name.endswith(">") && !Name.startswith("operator")) {
312+
if (appendTemplateParameters(D)) {
313+
if (EndedWithTemplate)
314+
OS << ' ';
315+
OS << '>';
316+
EndedWithTemplate = true;
317+
Word = true;
318+
}
276319
}
277-
OS << NamePtr;
278320
break;
279321
}
322+
}
280323
return Inner;
281324
}
282325

@@ -310,6 +353,13 @@ struct DWARFTypePrinter {
310353
DW_TAG_ptr_to_member_type);
311354
break;
312355
}
356+
/*
357+
case DW_TAG_structure_type:
358+
case DW_TAG_class_type:
359+
case DW_TAG_enumeration_type:
360+
case DW_TAG_base_type:
361+
case DW_TAG_namespace:
362+
*/
313363
default:
314364
break;
315365
}
@@ -325,6 +375,170 @@ struct DWARFTypePrinter {
325375
appendScopes(D.getParent());
326376
return appendUnqualifiedNameBefore(D);
327377
}
378+
bool appendTemplateParameters(DWARFDie D, bool *FirstParameter = nullptr) {
379+
bool FirstParameterValue = true;
380+
bool IsTemplate = false;
381+
if (!FirstParameter)
382+
FirstParameter = &FirstParameterValue;
383+
for (const DWARFDie &C : D) {
384+
auto Sep = [&] {
385+
if (*FirstParameter)
386+
OS << '<';
387+
else
388+
OS << ", ";
389+
IsTemplate = true;
390+
EndedWithTemplate = false;
391+
*FirstParameter = false;
392+
};
393+
if (C.getTag() == dwarf::DW_TAG_GNU_template_parameter_pack) {
394+
IsTemplate = true;
395+
appendTemplateParameters(C, FirstParameter);
396+
}
397+
if (C.getTag() == dwarf::DW_TAG_template_value_parameter) {
398+
DWARFDie T = C.getAttributeValueAsReferencedDie(DW_AT_type);
399+
Sep();
400+
if (T.getTag() == DW_TAG_enumeration_type) {
401+
auto V = C.find(DW_AT_const_value);
402+
bool FoundEnumerator = false;
403+
for (const DWARFDie &Enumerator : T) {
404+
auto EV = Enumerator.find(DW_AT_const_value);
405+
if (V && EV &&
406+
V->getAsSignedConstant() == EV->getAsSignedConstant()) {
407+
if (T.find(DW_AT_enum_class)) {
408+
appendQualifiedName(T);
409+
OS << "::";
410+
} else
411+
appendScopes(T.getParent());
412+
OS << Enumerator.getShortName();
413+
FoundEnumerator = true;
414+
break;
415+
}
416+
}
417+
if (FoundEnumerator)
418+
continue;
419+
OS << '(';
420+
appendQualifiedName(T);
421+
OS << ')';
422+
OS << to_string(*V->getAsSignedConstant());
423+
continue;
424+
}
425+
// /Maybe/ we could do pointer type parameters, looking for the
426+
// symbol in the ELF symbol table to get back to the variable...
427+
// but probably not worth it.
428+
if (T.getTag() == DW_TAG_pointer_type)
429+
continue;
430+
const char *RawName = dwarf::toString(T.find(DW_AT_name), nullptr);
431+
assert(RawName);
432+
StringRef Name = RawName;
433+
auto V = C.find(DW_AT_const_value);
434+
bool IsQualifiedChar = false;
435+
if (Name == "bool") {
436+
OS << (*V->getAsUnsignedConstant() ? "true" : "false");
437+
} else if (Name == "short") {
438+
OS << "(short)";
439+
OS << to_string(*V->getAsSignedConstant());
440+
} else if (Name == "unsigned short") {
441+
OS << "(unsigned short)";
442+
OS << to_string(*V->getAsSignedConstant());
443+
} else if (Name == "int")
444+
OS << to_string(*V->getAsSignedConstant());
445+
else if (Name == "long") {
446+
OS << to_string(*V->getAsSignedConstant());
447+
OS << "L";
448+
} else if (Name == "long long") {
449+
OS << to_string(*V->getAsSignedConstant());
450+
OS << "LL";
451+
} else if (Name == "unsigned int") {
452+
OS << to_string(*V->getAsUnsignedConstant());
453+
OS << "U";
454+
} else if (Name == "unsigned long") {
455+
OS << to_string(*V->getAsUnsignedConstant());
456+
OS << "UL";
457+
} else if (Name == "unsigned long long") {
458+
OS << to_string(*V->getAsUnsignedConstant());
459+
OS << "ULL";
460+
} else if (Name == "char" ||
461+
(IsQualifiedChar =
462+
(Name == "unsigned char" || Name == "signed char"))) {
463+
// FIXME: check T's DW_AT_type to see if it's signed or not (since
464+
// char signedness is implementation defined).
465+
auto Val = *V->getAsSignedConstant();
466+
// Copied/hacked up from Clang's CharacterLiteral::print - incomplete
467+
// (doesn't actually support different character types/widths, sign
468+
// handling's not done, and doesn't correctly test if a character is
469+
// printable or needs to use a numeric escape sequence instead)
470+
if (IsQualifiedChar) {
471+
OS << '(';
472+
OS << Name;
473+
OS << ')';
474+
}
475+
switch (Val) {
476+
case '\\':
477+
OS << "'\\\\'";
478+
break;
479+
case '\'':
480+
OS << "'\\''";
481+
break;
482+
case '\a':
483+
// TODO: K&R: the meaning of '\\a' is different in traditional C
484+
OS << "'\\a'";
485+
break;
486+
case '\b':
487+
OS << "'\\b'";
488+
break;
489+
case '\f':
490+
OS << "'\\f'";
491+
break;
492+
case '\n':
493+
OS << "'\\n'";
494+
break;
495+
case '\r':
496+
OS << "'\\r'";
497+
break;
498+
case '\t':
499+
OS << "'\\t'";
500+
break;
501+
case '\v':
502+
OS << "'\\v'";
503+
break;
504+
default:
505+
if ((Val & ~0xFFu) == ~0xFFu)
506+
Val &= 0xFFu;
507+
if (Val < 127 && Val >= 32) {
508+
OS << "'";
509+
OS << (char)Val;
510+
OS << "'";
511+
} else if (Val < 256)
512+
OS << to_string(llvm::format("'\\x%02x'", Val));
513+
else if (Val <= 0xFFFF)
514+
OS << to_string(llvm::format("'\\u%04x'", Val));
515+
else
516+
OS << to_string(llvm::format("'\\U%08x'", Val));
517+
}
518+
}
519+
continue;
520+
}
521+
if (C.getTag() == dwarf::DW_TAG_GNU_template_template_param) {
522+
const char *RawName =
523+
dwarf::toString(C.find(DW_AT_GNU_template_name), nullptr);
524+
assert(RawName);
525+
StringRef Name = RawName;
526+
Sep();
527+
OS << Name;
528+
continue;
529+
}
530+
if (C.getTag() != dwarf::DW_TAG_template_type_parameter)
531+
continue;
532+
auto TypeAttr = C.find(DW_AT_type);
533+
Sep();
534+
appendQualifiedName(TypeAttr
535+
? C.getAttributeValueAsReferencedDie(*TypeAttr)
536+
: DWARFDie());
537+
}
538+
if (IsTemplate && *FirstParameter && FirstParameter == &FirstParameterValue)
539+
OS << '<';
540+
return IsTemplate;
541+
}
328542
void decomposeConstVolatile(DWARFDie &N, DWARFDie &T, DWARFDie &C,
329543
DWARFDie &V) {
330544
(N.getTag() == DW_TAG_const_type ? C : V) = N;
@@ -386,10 +600,11 @@ struct DWARFTypePrinter {
386600
}
387601

388602
/// Recursively append the DIE type name when applicable.
389-
void appendUnqualifiedName(const DWARFDie &D) {
603+
void appendUnqualifiedName(DWARFDie D,
604+
std::string *OriginalFullName = nullptr) {
390605
// FIXME: We should have pretty printers per language. Currently we print
391606
// everything as if it was C++ and fall back to the TAG type name.
392-
DWARFDie Inner = appendUnqualifiedNameBefore(D);
607+
DWARFDie Inner = appendUnqualifiedNameBefore(D, OriginalFullName);
393608
appendUnqualifiedNameAfter(D, Inner);
394609
}
395610

@@ -586,6 +801,14 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die,
586801
OS << ")\n";
587802
}
588803

804+
void DWARFDie::getFullName(raw_string_ostream &OS,
805+
std::string *OriginalFullName) const {
806+
const char *NamePtr = getShortName();
807+
if (!NamePtr)
808+
return;
809+
DWARFTypePrinter(OS).appendUnqualifiedName(*this, OriginalFullName);
810+
}
811+
589812
bool DWARFDie::isSubprogramDIE() const { return getTag() == DW_TAG_subprogram; }
590813

591814
bool DWARFDie::isSubroutineDIE() const {

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
99
#include "llvm/ADT/SmallSet.h"
10+
#include "llvm/BinaryFormat/Dwarf.h"
1011
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
1112
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
1213
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
@@ -161,6 +162,26 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData,
161162
return Success;
162163
}
163164

165+
bool DWARFVerifier::verifyName(const DWARFDie &Die) {
166+
// FIXME Add some kind of record of which DIE names have already failed and
167+
// don't bother checking a DIE that uses an already failed DIE.
168+
169+
std::string ReconstructedName;
170+
raw_string_ostream OS(ReconstructedName);
171+
std::string OriginalFullName;
172+
Die.getFullName(OS, &OriginalFullName);
173+
OS.flush();
174+
if (!OriginalFullName.empty() && OriginalFullName != ReconstructedName) {
175+
error() << "Simplified template DW_AT_name could not be reconstituted:\n"
176+
<< formatv(" original: {0}\n"
177+
" reconstituted: {1}\n",
178+
OriginalFullName, ReconstructedName);
179+
dump(Die) << '\n';
180+
dump(Die.getDwarfUnit()->getUnitDIE()) << '\n';
181+
}
182+
return 0;
183+
}
184+
164185
unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,
165186
ReferenceMap &UnitLocalReferences,
166187
ReferenceMap &CrossUnitReferences) {
@@ -178,6 +199,8 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit,
178199
CrossUnitReferences);
179200
}
180201

202+
NumUnitErrors += verifyName(Die);
203+
181204
if (Die.hasChildren()) {
182205
if (Die.getFirstChild().isValid() &&
183206
Die.getFirstChild().getTag() == DW_TAG_null) {

0 commit comments

Comments
 (0)