llvm-symbolizer: Add a FRAME command.
This command prints a description of the referenced function's stack frame.
For each formal parameter and local variable, the tool prints:
- function name
- variable name
- file/line of declaration
- FP-relative variable location (if available)
- size in bytes
- HWASAN tag offset
This information will be used by the HWASAN runtime to identify local
variables in UAR reports.
Differential Revision: https://reviews.llvm.org/D63468
llvm-svn: 364225
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
index 09a42a6..321e157 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp
@@ -41,6 +41,7 @@
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetRegistry.h"
@@ -972,6 +973,124 @@
return FoundResult;
}
+static Optional<uint64_t> getTypeSize(DWARFDie Type, uint64_t PointerSize) {
+ if (auto SizeAttr = Type.find(DW_AT_byte_size))
+ if (Optional<uint64_t> Size = SizeAttr->getAsUnsignedConstant())
+ return Size;
+
+ switch (Type.getTag()) {
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_rvalue_reference_type:
+ return PointerSize;
+ case DW_TAG_ptr_to_member_type: {
+ if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type))
+ if (BaseType.getTag() == DW_TAG_subroutine_type)
+ return 2 * PointerSize;
+ return PointerSize;
+ }
+ case DW_TAG_const_type:
+ case DW_TAG_volatile_type:
+ case DW_TAG_restrict_type:
+ case DW_TAG_typedef: {
+ if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type))
+ return getTypeSize(BaseType, PointerSize);
+ break;
+ }
+ case DW_TAG_array_type: {
+ DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type);
+ if (!BaseType)
+ return Optional<uint64_t>();
+ Optional<uint64_t> BaseSize = getTypeSize(BaseType, PointerSize);
+ if (!BaseSize)
+ return Optional<uint64_t>();
+ uint64_t Size = *BaseSize;
+ for (DWARFDie Child : Type) {
+ if (Child.getTag() != DW_TAG_subrange_type)
+ continue;
+
+ if (auto ElemCountAttr = Child.find(DW_AT_count))
+ if (Optional<uint64_t> ElemCount =
+ ElemCountAttr->getAsUnsignedConstant())
+ Size *= *ElemCount;
+ if (auto UpperBoundAttr = Child.find(DW_AT_upper_bound))
+ if (Optional<int64_t> UpperBound =
+ UpperBoundAttr->getAsSignedConstant()) {
+ int64_t LowerBound = 0;
+ if (auto LowerBoundAttr = Child.find(DW_AT_lower_bound))
+ LowerBound = LowerBoundAttr->getAsSignedConstant().getValueOr(0);
+ Size *= *UpperBound - LowerBound + 1;
+ }
+ }
+ return Size;
+ }
+ default:
+ break;
+ }
+ return Optional<uint64_t>();
+}
+
+void DWARFContext::addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram,
+ DWARFDie Die, std::vector<DILocal> &Result) {
+ if (Die.getTag() == DW_TAG_variable ||
+ Die.getTag() == DW_TAG_formal_parameter) {
+ DILocal Local;
+ if (auto NameAttr = Subprogram.find(DW_AT_name))
+ if (Optional<const char *> Name = NameAttr->getAsCString())
+ Local.FunctionName = *Name;
+ if (auto LocationAttr = Die.find(DW_AT_location))
+ if (Optional<ArrayRef<uint8_t>> Location = LocationAttr->getAsBlock())
+ if (!Location->empty() && (*Location)[0] == DW_OP_fbreg)
+ Local.FrameOffset =
+ decodeSLEB128(Location->data() + 1, nullptr, Location->end());
+ if (auto TagOffsetAttr = Die.find(DW_AT_LLVM_tag_offset))
+ Local.TagOffset = TagOffsetAttr->getAsUnsignedConstant();
+
+ if (auto Origin =
+ Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin))
+ Die = Origin;
+ if (auto NameAttr = Die.find(DW_AT_name))
+ if (Optional<const char *> Name = NameAttr->getAsCString())
+ Local.Name = *Name;
+ if (auto Type = Die.getAttributeValueAsReferencedDie(DW_AT_type))
+ Local.Size = getTypeSize(Type, getCUAddrSize());
+ if (auto DeclFileAttr = Die.find(DW_AT_decl_file)) {
+ if (const auto *LT = CU->getContext().getLineTableForUnit(CU))
+ LT->getFileNameByIndex(
+ DeclFileAttr->getAsUnsignedConstant().getValue(),
+ CU->getCompilationDir(),
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
+ Local.DeclFile);
+ }
+ if (auto DeclLineAttr = Die.find(DW_AT_decl_line))
+ Local.DeclLine = DeclLineAttr->getAsUnsignedConstant().getValue();
+
+ Result.push_back(Local);
+ return;
+ }
+
+ if (Die.getTag() == DW_TAG_inlined_subroutine)
+ if (auto Origin =
+ Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin))
+ Subprogram = Origin;
+
+ for (auto Child : Die)
+ addLocalsForDie(CU, Subprogram, Child, Result);
+}
+
+std::vector<DILocal>
+DWARFContext::getLocalsForAddress(object::SectionedAddress Address) {
+ std::vector<DILocal> Result;
+ DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address);
+ if (!CU)
+ return Result;
+
+ DWARFDie Subprogram = CU->getSubroutineForAddress(Address.Address);
+ if (Subprogram.isValid())
+ addLocalsForDie(CU, Subprogram, Subprogram, Result);
+ return Result;
+}
+
DILineInfo DWARFContext::getLineInfoForAddress(object::SectionedAddress Address,
DILineInfoSpecifier Spec) {
DILineInfo Result;
diff --git a/llvm/lib/DebugInfo/PDB/PDBContext.cpp b/llvm/lib/DebugInfo/PDB/PDBContext.cpp
index 89c20e6..e452f1d 100644
--- a/llvm/lib/DebugInfo/PDB/PDBContext.cpp
+++ b/llvm/lib/DebugInfo/PDB/PDBContext.cpp
@@ -91,6 +91,11 @@
return InlineInfo;
}
+std::vector<DILocal>
+PDBContext::getLocalsForAddress(object::SectionedAddress Address) {
+ return std::vector<DILocal>();
+}
+
std::string PDBContext::getFunctionName(uint64_t Address,
DINameKind NameKind) const {
if (NameKind == DINameKind::None)
diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
index f6c7ef8..b2bfef2 100644
--- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
+++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp
@@ -122,5 +122,28 @@
return *this;
}
+DIPrinter &DIPrinter::operator<<(const DILocal &Local) {
+ OS << Local.FunctionName << '\n';
+ OS << Local.Name << '\n';
+ if (Local.DeclFile.empty())
+ OS << "??";
+ else
+ OS << Local.DeclFile;
+ OS << ':' << Local.DeclLine << '\n';
+ if (Local.FrameOffset)
+ OS << *Local.FrameOffset << ' ';
+ else
+ OS << "?? ";
+ if (Local.Size)
+ OS << *Local.Size << ' ';
+ else
+ OS << "?? ";
+ if (Local.TagOffset)
+ OS << *Local.TagOffset << '\n';
+ else
+ OS << "??\n";
+ return *this;
+}
+
} // end namespace symbolize
} // end namespace llvm
diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp
index 62f849f..6092584 100644
--- a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp
+++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp
@@ -298,6 +298,14 @@
return Res;
}
+std::vector<DILocal> SymbolizableObjectFile::symbolizeFrame(
+ object::SectionedAddress ModuleOffset) const {
+ if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection)
+ ModuleOffset.SectionIndex =
+ getModuleSectionIndexForAddress(ModuleOffset.Address);
+ return DebugInfoContext->getLocalsForAddress(ModuleOffset);
+}
+
/// Search for the first occurence of specified Address in ObjectFile.
uint64_t SymbolizableObjectFile::getModuleSectionIndexForAddress(
uint64_t Address) const {
diff --git a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h
index d5510cb..3a511dc 100644
--- a/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h
+++ b/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.h
@@ -40,6 +40,8 @@
FunctionNameKind FNKind,
bool UseSymbolTable) const override;
DIGlobal symbolizeData(object::SectionedAddress ModuleOffset) const override;
+ std::vector<DILocal>
+ symbolizeFrame(object::SectionedAddress ModuleOffset) const override;
// Return true if this is a 32-bit x86 PE COFF module.
bool isWin32Module() const override;
diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
index 1bfd4e8..1cb8a7a 100644
--- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
+++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
@@ -133,6 +133,29 @@
return Global;
}
+Expected<std::vector<DILocal>>
+LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName,
+ object::SectionedAddress ModuleOffset) {
+ SymbolizableModule *Info;
+ if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
+ Info = InfoOrErr.get();
+ else
+ return InfoOrErr.takeError();
+
+ // A null module means an error has already been reported. Return an empty
+ // result.
+ if (!Info)
+ return std::vector<DILocal>();
+
+ // If the user is giving us relative addresses, add the preferred base of
+ // the object to the offset before we do the query. It's what DIContext
+ // expects.
+ if (Opts.RelativeAddresses)
+ ModuleOffset.Address += Info->getModulePreferredBase();
+
+ return Info->symbolizeFrame(ModuleOffset);
+}
+
void LLVMSymbolizer::flush() {
ObjectForUBPathAndArch.clear();
BinaryForPath.clear();