blob: 7746bc508b41af7a3789b662fafb7d5feb6bd0d5 [file] [log] [blame]
//===-- LVELFReader.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This implements the LVELFReader class.
// It supports ELF and Mach-O formats.
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h"
#include "llvm/DebugInfo/DIContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
#include "llvm/Object/Error.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/FormatVariadic.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::logicalview;
#define DEBUG_TYPE "ElfReader"
LVElement *LVELFReader::createElement(dwarf::Tag Tag) {
CurrentScope = nullptr;
CurrentSymbol = nullptr;
CurrentType = nullptr;
CurrentRanges.clear();
if (!options().getPrintSymbols()) {
switch (Tag) {
// As the command line options did not specify a request to print
// logical symbols (--print=symbols or --print=all or --print=elements),
// skip its creation.
case dwarf::DW_TAG_formal_parameter:
case dwarf::DW_TAG_unspecified_parameters:
case dwarf::DW_TAG_member:
case dwarf::DW_TAG_variable:
case dwarf::DW_TAG_inheritance:
case dwarf::DW_TAG_constant:
case dwarf::DW_TAG_call_site_parameter:
case dwarf::DW_TAG_GNU_call_site_parameter:
return nullptr;
default:
break;
}
}
switch (Tag) {
// Types.
case dwarf::DW_TAG_base_type:
CurrentType = new LVType();
CurrentType->setIsBase();
if (options().getAttributeBase())
CurrentType->setIncludeInPrint();
return CurrentType;
case dwarf::DW_TAG_const_type:
CurrentType = new LVType();
CurrentType->setIsConst();
CurrentType->setName("const");
return CurrentType;
case dwarf::DW_TAG_enumerator:
CurrentType = new LVTypeEnumerator();
return CurrentType;
case dwarf::DW_TAG_imported_declaration:
CurrentType = new LVTypeImport();
CurrentType->setIsImportDeclaration();
return CurrentType;
case dwarf::DW_TAG_imported_module:
CurrentType = new LVTypeImport();
CurrentType->setIsImportModule();
return CurrentType;
case dwarf::DW_TAG_pointer_type:
CurrentType = new LVType();
CurrentType->setIsPointer();
CurrentType->setName("*");
return CurrentType;
case dwarf::DW_TAG_ptr_to_member_type:
CurrentType = new LVType();
CurrentType->setIsPointerMember();
CurrentType->setName("*");
return CurrentType;
case dwarf::DW_TAG_reference_type:
CurrentType = new LVType();
CurrentType->setIsReference();
CurrentType->setName("&");
return CurrentType;
case dwarf::DW_TAG_restrict_type:
CurrentType = new LVType();
CurrentType->setIsRestrict();
CurrentType->setName("restrict");
return CurrentType;
case dwarf::DW_TAG_rvalue_reference_type:
CurrentType = new LVType();
CurrentType->setIsRvalueReference();
CurrentType->setName("&&");
return CurrentType;
case dwarf::DW_TAG_subrange_type:
CurrentType = new LVTypeSubrange();
return CurrentType;
case dwarf::DW_TAG_template_value_parameter:
CurrentType = new LVTypeParam();
CurrentType->setIsTemplateValueParam();
return CurrentType;
case dwarf::DW_TAG_template_type_parameter:
CurrentType = new LVTypeParam();
CurrentType->setIsTemplateTypeParam();
return CurrentType;
case dwarf::DW_TAG_GNU_template_template_param:
CurrentType = new LVTypeParam();
CurrentType->setIsTemplateTemplateParam();
return CurrentType;
case dwarf::DW_TAG_typedef:
CurrentType = new LVTypeDefinition();
return CurrentType;
case dwarf::DW_TAG_unspecified_type:
CurrentType = new LVType();
CurrentType->setIsUnspecified();
return CurrentType;
case dwarf::DW_TAG_volatile_type:
CurrentType = new LVType();
CurrentType->setIsVolatile();
CurrentType->setName("volatile");
return CurrentType;
// Symbols.
case dwarf::DW_TAG_formal_parameter:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsParameter();
return CurrentSymbol;
case dwarf::DW_TAG_unspecified_parameters:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsUnspecified();
CurrentSymbol->setName("...");
return CurrentSymbol;
case dwarf::DW_TAG_member:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsMember();
return CurrentSymbol;
case dwarf::DW_TAG_variable:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsVariable();
return CurrentSymbol;
case dwarf::DW_TAG_inheritance:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsInheritance();
return CurrentSymbol;
case dwarf::DW_TAG_call_site_parameter:
case dwarf::DW_TAG_GNU_call_site_parameter:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsCallSiteParameter();
return CurrentSymbol;
case dwarf::DW_TAG_constant:
CurrentSymbol = new LVSymbol();
CurrentSymbol->setIsConstant();
return CurrentSymbol;
// Scopes.
case dwarf::DW_TAG_catch_block:
CurrentScope = new LVScope();
CurrentScope->setIsCatchBlock();
return CurrentScope;
case dwarf::DW_TAG_lexical_block:
CurrentScope = new LVScope();
CurrentScope->setIsLexicalBlock();
return CurrentScope;
case dwarf::DW_TAG_try_block:
CurrentScope = new LVScope();
CurrentScope->setIsTryBlock();
return CurrentScope;
case dwarf::DW_TAG_compile_unit:
case dwarf::DW_TAG_skeleton_unit:
CurrentScope = new LVScopeCompileUnit();
CompileUnit = static_cast<LVScopeCompileUnit *>(CurrentScope);
return CurrentScope;
case dwarf::DW_TAG_inlined_subroutine:
CurrentScope = new LVScopeFunctionInlined();
return CurrentScope;
case dwarf::DW_TAG_namespace:
CurrentScope = new LVScopeNamespace();
return CurrentScope;
case dwarf::DW_TAG_template_alias:
CurrentScope = new LVScopeAlias();
return CurrentScope;
case dwarf::DW_TAG_array_type:
CurrentScope = new LVScopeArray();
return CurrentScope;
case dwarf::DW_TAG_call_site:
case dwarf::DW_TAG_GNU_call_site:
CurrentScope = new LVScopeFunction();
CurrentScope->setIsCallSite();
return CurrentScope;
case dwarf::DW_TAG_entry_point:
CurrentScope = new LVScopeFunction();
CurrentScope->setIsEntryPoint();
return CurrentScope;
case dwarf::DW_TAG_subprogram:
CurrentScope = new LVScopeFunction();
CurrentScope->setIsSubprogram();
return CurrentScope;
case dwarf::DW_TAG_subroutine_type:
CurrentScope = new LVScopeFunctionType();
return CurrentScope;
case dwarf::DW_TAG_label:
CurrentScope = new LVScopeFunction();
CurrentScope->setIsLabel();
return CurrentScope;
case dwarf::DW_TAG_class_type:
CurrentScope = new LVScopeAggregate();
CurrentScope->setIsClass();
return CurrentScope;
case dwarf::DW_TAG_structure_type:
CurrentScope = new LVScopeAggregate();
CurrentScope->setIsStructure();
return CurrentScope;
case dwarf::DW_TAG_union_type:
CurrentScope = new LVScopeAggregate();
CurrentScope->setIsUnion();
return CurrentScope;
case dwarf::DW_TAG_enumeration_type:
CurrentScope = new LVScopeEnumeration();
return CurrentScope;
case dwarf::DW_TAG_GNU_formal_parameter_pack:
CurrentScope = new LVScopeFormalPack();
return CurrentScope;
case dwarf::DW_TAG_GNU_template_parameter_pack:
CurrentScope = new LVScopeTemplatePack();
return CurrentScope;
default:
// Collect TAGs not implemented.
if (options().getInternalTag() && Tag)
CompileUnit->addDebugTag(Tag, CurrentOffset);
break;
}
return nullptr;
}
void LVELFReader::processOneAttribute(const DWARFDie &Die, LVOffset *OffsetPtr,
const AttributeSpec &AttrSpec) {
uint64_t OffsetOnEntry = *OffsetPtr;
DWARFUnit *U = Die.getDwarfUnit();
const DWARFFormValue &FormValue =
DWARFFormValue::createFromUnit(AttrSpec.Form, U, OffsetPtr);
// We are processing .debug_info section, implicit_const attribute
// values are not really stored here, but in .debug_abbrev section.
auto GetAsUnsignedConstant = [&]() -> int64_t {
return AttrSpec.isImplicitConst() ? AttrSpec.getImplicitConstValue()
: *FormValue.getAsUnsignedConstant();
};
auto GetFlag = [](const DWARFFormValue &FormValue) -> bool {
return FormValue.isFormClass(DWARFFormValue::FC_Flag);
};
auto GetBoundValue = [](const DWARFFormValue &FormValue) -> int64_t {
switch (FormValue.getForm()) {
case dwarf::DW_FORM_ref_addr:
case dwarf::DW_FORM_ref1:
case dwarf::DW_FORM_ref2:
case dwarf::DW_FORM_ref4:
case dwarf::DW_FORM_ref8:
case dwarf::DW_FORM_ref_udata:
case dwarf::DW_FORM_ref_sig8:
return *FormValue.getAsReferenceUVal();
case dwarf::DW_FORM_data1:
case dwarf::DW_FORM_flag:
case dwarf::DW_FORM_data2:
case dwarf::DW_FORM_data4:
case dwarf::DW_FORM_data8:
case dwarf::DW_FORM_udata:
case dwarf::DW_FORM_ref_sup4:
case dwarf::DW_FORM_ref_sup8:
return *FormValue.getAsUnsignedConstant();
case dwarf::DW_FORM_sdata:
return *FormValue.getAsSignedConstant();
default:
return 0;
}
};
LLVM_DEBUG({
dbgs() << " " << hexValue(OffsetOnEntry)
<< formatv(" {0}", AttrSpec.Attr) << "\n";
});
switch (AttrSpec.Attr) {
case dwarf::DW_AT_accessibility:
CurrentElement->setAccessibilityCode(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_artificial:
CurrentElement->setIsArtificial();
break;
case dwarf::DW_AT_bit_size:
CurrentElement->setBitSize(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_call_file:
CurrentElement->setCallFilenameIndex(GetAsUnsignedConstant());
break;
case dwarf::DW_AT_call_line:
CurrentElement->setCallLineNumber(IncrementFileIndex
? GetAsUnsignedConstant() + 1
: GetAsUnsignedConstant());
break;
case dwarf::DW_AT_comp_dir:
CompileUnit->setCompilationDirectory(dwarf::toStringRef(FormValue));
break;
case dwarf::DW_AT_const_value:
if (FormValue.isFormClass(DWARFFormValue::FC_Block)) {
ArrayRef<uint8_t> Expr = *FormValue.getAsBlock();
// Store the expression as a hexadecimal string.
CurrentElement->setValue(
llvm::toHex(llvm::toStringRef(Expr), /*LowerCase=*/true));
} else if (FormValue.isFormClass(DWARFFormValue::FC_Constant)) {
// In the case of negative values, generate the string representation
// for a positive value prefixed with the negative sign.
if (FormValue.getForm() == dwarf::DW_FORM_sdata) {
std::stringstream Stream;
int64_t Value = *FormValue.getAsSignedConstant();
if (Value < 0) {
Stream << "-";
Value = std::abs(Value);
}
Stream << hexString(Value, 2);
CurrentElement->setValue(Stream.str());
} else
CurrentElement->setValue(
hexString(*FormValue.getAsUnsignedConstant(), 2));
} else
CurrentElement->setValue(dwarf::toStringRef(FormValue));
break;
case dwarf::DW_AT_count:
CurrentElement->setCount(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_decl_line:
CurrentElement->setLineNumber(GetAsUnsignedConstant());
break;
case dwarf::DW_AT_decl_file:
CurrentElement->setFilenameIndex(IncrementFileIndex
? GetAsUnsignedConstant() + 1
: GetAsUnsignedConstant());
break;
case dwarf::DW_AT_enum_class:
if (GetFlag(FormValue))
CurrentElement->setIsEnumClass();
break;
case dwarf::DW_AT_external:
if (GetFlag(FormValue))
CurrentElement->setIsExternal();
break;
case dwarf::DW_AT_GNU_discriminator:
CurrentElement->setDiscriminator(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_inline:
CurrentElement->setInlineCode(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_lower_bound:
CurrentElement->setLowerBound(GetBoundValue(FormValue));
break;
case dwarf::DW_AT_name:
CurrentElement->setName(dwarf::toStringRef(FormValue));
break;
case dwarf::DW_AT_linkage_name:
case dwarf::DW_AT_MIPS_linkage_name:
CurrentElement->setLinkageName(dwarf::toStringRef(FormValue));
break;
case dwarf::DW_AT_producer:
if (options().getAttributeProducer())
CurrentElement->setProducer(dwarf::toStringRef(FormValue));
break;
case dwarf::DW_AT_upper_bound:
CurrentElement->setUpperBound(GetBoundValue(FormValue));
break;
case dwarf::DW_AT_virtuality:
CurrentElement->setVirtualityCode(*FormValue.getAsUnsignedConstant());
break;
case dwarf::DW_AT_abstract_origin:
case dwarf::DW_AT_call_origin:
case dwarf::DW_AT_extension:
case dwarf::DW_AT_import:
case dwarf::DW_AT_specification:
case dwarf::DW_AT_type:
updateReference(AttrSpec.Attr, FormValue);
break;
case dwarf::DW_AT_low_pc:
if (options().getGeneralCollectRanges()) {
FoundLowPC = true;
// For toolchains that support the removal of unused code, the linker
// marks functions that have been removed, by setting the value for the
// low_pc to the max address.
if (std::optional<uint64_t> Value = FormValue.getAsAddress()) {
CurrentLowPC = *Value;
} else {
uint64_t UValue = FormValue.getRawUValue();
if (U->getAddrOffsetSectionItem(UValue)) {
CurrentLowPC = *FormValue.getAsAddress();
} else {
FoundLowPC = false;
// We are dealing with an index into the .debug_addr section.
LLVM_DEBUG({
dbgs() << format("indexed (%8.8x) address = ", (uint32_t)UValue);
});
}
}
if (FoundLowPC) {
if (CurrentLowPC == MaxAddress)
CurrentElement->setIsDiscarded();
if (CurrentElement->isCompileUnit())
setCUBaseAddress(CurrentLowPC);
}
}
break;
case dwarf::DW_AT_high_pc:
if (options().getGeneralCollectRanges()) {
FoundHighPC = true;
if (std::optional<uint64_t> Address = FormValue.getAsAddress())
// High PC is an address.
CurrentHighPC = *Address;
if (std::optional<uint64_t> Offset = FormValue.getAsUnsignedConstant())
// High PC is an offset from LowPC.
CurrentHighPC = CurrentLowPC + *Offset;
// Store the real upper limit for the address range.
if (UpdateHighAddress && CurrentHighPC > 0)
--CurrentHighPC;
if (CurrentElement->isCompileUnit())
setCUHighAddress(CurrentHighPC);
}
break;
case dwarf::DW_AT_ranges:
if (RangesDataAvailable && options().getGeneralCollectRanges()) {
auto GetRanges = [](const DWARFFormValue &FormValue,
DWARFUnit *U) -> Expected<DWARFAddressRangesVector> {
if (FormValue.getForm() == dwarf::DW_FORM_rnglistx)
return U->findRnglistFromIndex(*FormValue.getAsSectionOffset());
return U->findRnglistFromOffset(*FormValue.getAsSectionOffset());
};
Expected<DWARFAddressRangesVector> RangesOrError =
GetRanges(FormValue, U);
if (!RangesOrError) {
LLVM_DEBUG({
std::string TheError(toString(RangesOrError.takeError()));
dbgs() << format("error decoding address ranges = ",
TheError.c_str());
});
consumeError(RangesOrError.takeError());
break;
}
// The address ranges are absolute. There is no need to add any addend.
DWARFAddressRangesVector Ranges = RangesOrError.get();
for (DWARFAddressRange &Range : Ranges) {
// This seems to be a tombstone for empty ranges.
if (Range.LowPC == Range.HighPC)
continue;
// Store the real upper limit for the address range.
if (UpdateHighAddress && Range.HighPC > 0)
--Range.HighPC;
// Add the pair of addresses.
CurrentScope->addObject(Range.LowPC, Range.HighPC);
// If the scope is the CU, do not update the ranges set.
if (!CurrentElement->isCompileUnit())
CurrentRanges.emplace_back(Range.LowPC, Range.HighPC);
}
}
break;
// Get the location list for the symbol.
case dwarf::DW_AT_data_member_location:
if (options().getAttributeAnyLocation())
processLocationMember(AttrSpec.Attr, FormValue, Die, OffsetOnEntry);
break;
// Get the location list for the symbol.
case dwarf::DW_AT_location:
case dwarf::DW_AT_string_length:
case dwarf::DW_AT_use_location:
if (options().getAttributeAnyLocation() && CurrentSymbol)
processLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry);
break;
case dwarf::DW_AT_call_data_value:
case dwarf::DW_AT_call_value:
case dwarf::DW_AT_GNU_call_site_data_value:
case dwarf::DW_AT_GNU_call_site_value:
if (options().getAttributeAnyLocation() && CurrentSymbol)
processLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry,
/*CallSiteLocation=*/true);
break;
default:
break;
}
}
LVScope *LVELFReader::processOneDie(const DWARFDie &InputDIE, LVScope *Parent,
DWARFDie &SkeletonDie) {
// If the input DIE corresponds to the compile unit, it can be:
// a) Simple DWARF: a standard DIE. Ignore the skeleton DIE (is empty).
// b) Split DWARF: the DIE for the split DWARF. The skeleton is the DIE
// for the skeleton DWARF. Process both DIEs.
const DWARFDie &DIE = SkeletonDie.isValid() ? SkeletonDie : InputDIE;
DWARFDataExtractor DebugInfoData =
DIE.getDwarfUnit()->getDebugInfoExtractor();
LVOffset Offset = DIE.getOffset();
// Reset values for the current DIE.
CurrentLowPC = 0;
CurrentHighPC = 0;
CurrentOffset = Offset;
CurrentEndOffset = 0;
FoundLowPC = false;
FoundHighPC = false;
// Process supported attributes.
if (DebugInfoData.isValidOffset(Offset)) {
LLVM_DEBUG({
dbgs() << "DIE: " << hexValue(Offset) << formatv(" {0}", DIE.getTag())
<< "\n";
});
// Create the logical view element for the current DIE.
dwarf::Tag Tag = DIE.getTag();
CurrentElement = createElement(Tag);
if (!CurrentElement)
return CurrentScope;
CurrentElement->setTag(Tag);
CurrentElement->setOffset(Offset);
if (options().getAttributeAnySource() && CurrentElement->isCompileUnit())
addCompileUnitOffset(Offset,
static_cast<LVScopeCompileUnit *>(CurrentElement));
// Insert the newly created element into the element symbol table. If the
// element is in the list, it means there are previously created elements
// referencing this element.
if (ElementTable.find(Offset) == ElementTable.end()) {
// No previous references to this offset.
ElementTable.emplace(
std::piecewise_construct, std::forward_as_tuple(Offset),
std::forward_as_tuple(CurrentElement, LVElementSet()));
} else {
// There are previous references to this element. We need to update the
// element and all the references pointing to this element.
LVElementEntry &Reference = ElementTable[Offset];
Reference.first = CurrentElement;
// Traverse the element set and update the elements (backtracking).
// Using the bit associated with 'type' or 'reference' allows us to set
// the correct target.
for (LVElement *Target : Reference.second)
Target->getHasReference() ? Target->setReference(CurrentElement)
: Target->setType(CurrentElement);
// Clear the pending elements.
Reference.second.clear();
}
// Add the current element to its parent as there are attributes
// (locations) that require the scope level.
if (CurrentScope)
Parent->addElement(CurrentScope);
else if (CurrentSymbol)
Parent->addElement(CurrentSymbol);
else if (CurrentType)
Parent->addElement(CurrentType);
// Process the attributes for the given DIE.
auto ProcessAttributes = [&](const DWARFDie &TheDIE,
DWARFDataExtractor &DebugData) {
CurrentEndOffset = Offset;
uint32_t abbrCode = DebugData.getULEB128(&CurrentEndOffset);
if (abbrCode) {
if (const DWARFAbbreviationDeclaration *AbbrevDecl =
TheDIE.getAbbreviationDeclarationPtr())
if (AbbrevDecl)
for (const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec :
AbbrevDecl->attributes())
processOneAttribute(TheDIE, &CurrentEndOffset, AttrSpec);
}
};
ProcessAttributes(DIE, DebugInfoData);
// If the input DIE is for a compile unit, process its attributes in
// the case of split DWARF, to override any common attribute values.
if (SkeletonDie.isValid()) {
DWARFDataExtractor DebugInfoData =
InputDIE.getDwarfUnit()->getDebugInfoExtractor();
LVOffset Offset = InputDIE.getOffset();
if (DebugInfoData.isValidOffset(Offset))
ProcessAttributes(InputDIE, DebugInfoData);
}
}
if (CurrentScope) {
if (CurrentScope->getCanHaveRanges()) {
// If the scope has ranges, they are already added to the scope.
// Add any collected LowPC/HighPC values.
bool IsCompileUnit = CurrentScope->getIsCompileUnit();
if (FoundLowPC && FoundHighPC) {
CurrentScope->addObject(CurrentLowPC, CurrentHighPC);
if (!IsCompileUnit) {
// If the scope is a function, add it to the public names.
if ((options().getAttributePublics() ||
options().getPrintAnyLine()) &&
CurrentScope->getIsFunction() &&
!CurrentScope->getIsInlinedFunction())
CompileUnit->addPublicName(CurrentScope, CurrentLowPC,
CurrentHighPC);
}
}
// Look for scopes with ranges and no linkage name information that
// are referencing another scopes via DW_AT_specification. They are
// possible candidates for a comdat scope.
if (CurrentScope->getHasRanges() &&
!CurrentScope->getLinkageNameIndex() &&
CurrentScope->getHasReferenceSpecification()) {
// Get the linkage name in order to search for a possible comdat.
std::optional<DWARFFormValue> LinkageDIE =
DIE.findRecursively(dwarf::DW_AT_linkage_name);
if (LinkageDIE.has_value()) {
StringRef Name(dwarf::toStringRef(LinkageDIE));
if (!Name.empty())
CurrentScope->setLinkageName(Name);
}
}
// If the current scope is in the 'LinkageNames' table, update its
// logical scope. For other scopes, always we will assume the default
// ".text" section index.
LVSectionIndex SectionIndex = updateSymbolTable(CurrentScope);
if (CurrentScope->getIsComdat())
CompileUnit->setHasComdatScopes();
// Update section index contained ranges.
if (SectionIndex) {
if (!CurrentRanges.empty()) {
for (LVAddressRange &Range : CurrentRanges)
addSectionRange(SectionIndex, CurrentScope, Range.first,
Range.second);
CurrentRanges.clear();
}
// If the scope is the CU, do not update the ranges set.
if (FoundLowPC && FoundHighPC && !IsCompileUnit) {
addSectionRange(SectionIndex, CurrentScope, CurrentLowPC,
CurrentHighPC);
}
}
}
// Mark member functions.
if (Parent->getIsAggregate())
CurrentScope->setIsMember();
}
// Keep track of symbols with locations.
if (options().getAttributeAnyLocation() && CurrentSymbol &&
CurrentSymbol->getHasLocation())
SymbolsWithLocations.push_back(CurrentSymbol);
// If we have template parameters, mark the parent as template.
if (CurrentType && CurrentType->getIsTemplateParam())
Parent->setIsTemplate();
return CurrentScope;
}
void LVELFReader::traverseDieAndChildren(DWARFDie &DIE, LVScope *Parent,
DWARFDie &SkeletonDie) {
// Process the current DIE.
LVScope *Scope = processOneDie(DIE, Parent, SkeletonDie);
if (Scope) {
LVOffset Lower = DIE.getOffset();
LVOffset Upper = CurrentEndOffset;
DWARFDie DummyDie;
// Traverse the children chain.
DWARFDie Child = DIE.getFirstChild();
while (Child) {
traverseDieAndChildren(Child, Scope, DummyDie);
Upper = Child.getOffset();
Child = Child.getSibling();
}
// Calculate contributions to the debug info section.
if (options().getPrintSizes() && Upper)
CompileUnit->addSize(Scope, Lower, Upper);
}
}
void LVELFReader::processLocationGaps() {
if (options().getAttributeAnyLocation())
for (LVSymbol *Symbol : SymbolsWithLocations)
Symbol->fillLocationGaps();
}
void LVELFReader::createLineAndFileRecords(
const DWARFDebugLine::LineTable *Lines) {
if (!Lines)
return;
// Get the source filenames.
if (!Lines->Prologue.FileNames.empty())
for (const DWARFDebugLine::FileNameEntry &Entry :
Lines->Prologue.FileNames) {
std::string Directory;
if (Lines->getDirectoryForEntry(Entry, Directory))
Directory = transformPath(Directory);
if (Directory.empty())
Directory = std::string(CompileUnit->getCompilationDirectory());
std::string File = transformPath(dwarf::toStringRef(Entry.Name));
std::string String;
raw_string_ostream(String) << Directory << "/" << File;
CompileUnit->addFilename(String);
}
// In DWARF5 the file indexes start at 0;
bool IncrementIndex = Lines->Prologue.getVersion() >= 5;
// Get the source lines if requested by command line option.
if (options().getPrintLines() && Lines->Rows.size())
for (const DWARFDebugLine::Row &Row : Lines->Rows) {
// Here we collect logical debug lines in CULines. Later on,
// the 'processLines()' function will move each created logical line
// to its enclosing logical scope, using the debug ranges information
// and they will be released when its scope parent is deleted.
LVLineDebug *Line = new LVLineDebug();
CULines.push_back(Line);
Line->setAddress(Row.Address.Address);
Line->setFilename(
CompileUnit->getFilename(IncrementIndex ? Row.File + 1 : Row.File));
Line->setLineNumber(Row.Line);
if (Row.Discriminator)
Line->setDiscriminator(Row.Discriminator);
if (Row.IsStmt)
Line->setIsNewStatement();
if (Row.BasicBlock)
Line->setIsBasicBlock();
if (Row.EndSequence)
Line->setIsEndSequence();
if (Row.EpilogueBegin)
Line->setIsEpilogueBegin();
if (Row.PrologueEnd)
Line->setIsPrologueEnd();
LLVM_DEBUG({
dbgs() << "Address: " << hexValue(Line->getAddress())
<< " Line: " << Line->lineNumberAsString(/*ShowZero=*/true)
<< "\n";
});
}
}
std::string LVELFReader::getRegisterName(LVSmall Opcode, uint64_t Operands[2]) {
// The 'prettyPrintRegisterOp' function uses the DWARFUnit to support
// DW_OP_regval_type. At this point we are operating on a logical view
// item, with no access to the underlying DWARF data used by LLVM.
// We do not support DW_OP_regval_type here.
if (Opcode == dwarf::DW_OP_regval_type)
return {};
std::string string;
raw_string_ostream Stream(string);
DIDumpOptions DumpOpts;
auto *MCRegInfo = MRI.get();
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef {
if (!MCRegInfo)
return {};
if (std::optional<unsigned> LLVMRegNum =
MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH))
if (const char *RegName = MCRegInfo->getName(*LLVMRegNum))
return StringRef(RegName);
return {};
};
DumpOpts.GetNameForDWARFReg = GetRegName;
DWARFExpression::prettyPrintRegisterOp(/*U=*/nullptr, Stream, DumpOpts,
Opcode, Operands);
return Stream.str();
}
Error LVELFReader::createScopes() {
LLVM_DEBUG({
W.startLine() << "\n";
W.printString("File", Obj.getFileName().str());
W.printString("Format", FileFormatName);
});
if (Error Err = LVReader::createScopes())
return Err;
// As the DwarfContext object is valid only during the scopes creation,
// we need to create our own Target information, to be used during the
// logical view printing, in the case of instructions being requested.
std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(Obj);
if (!DwarfContext)
return createStringError(errc::invalid_argument,
"Could not create DWARF information: %s",
getFilename().str().c_str());
if (Error Err = loadTargetInfo(Obj))
return Err;
// Create a mapping for virtual addresses.
mapVirtualAddress(Obj);
// Select the correct compile unit range, depending if we are dealing with
// a standard or split DWARF object.
DWARFContext::compile_unit_range CompileUnits =
DwarfContext->getNumCompileUnits() ? DwarfContext->compile_units()
: DwarfContext->dwo_compile_units();
for (const std::unique_ptr<DWARFUnit> &CU : CompileUnits) {
// Deduction of index used for the line records.
//
// For the following test case: test.cpp
// void foo(void ParamPtr) { }
// Both GCC and Clang generate DWARF-5 .debug_line layout.
// * GCC (GNU C++17 11.3.0) - All DW_AT_decl_file use index 1.
//
// .debug_info:
// format = DWARF32, version = 0x0005
// DW_TAG_compile_unit
// DW_AT_name ("test.cpp")
// DW_TAG_subprogram ("foo")
// DW_AT_decl_file (1)
// DW_TAG_formal_parameter ("ParamPtr")
// DW_AT_decl_file (1)
// .debug_line:
// Line table prologue: format (DWARF32), version (5)
// include_directories[0] = "..."
// file_names[0]: name ("test.cpp"), dir_index (0)
// file_names[1]: name ("test.cpp"), dir_index (0)
// * Clang (14.0.6) - All DW_AT_decl_file use index 0.
//
// .debug_info:
// format = DWARF32, version = 0x0005
// DW_AT_producer ("clang version 14.0.6")
// DW_AT_name ("test.cpp")
//
// DW_TAG_subprogram ("foo")
// DW_AT_decl_file (0)
// DW_TAG_formal_parameter ("ParamPtr")
// DW_AT_decl_file (0)
// .debug_line:
// Line table prologue: format (DWARF32), version (5)
// include_directories[0] = "..."
// file_names[0]: name ("test.cpp"), dir_index (0)
// From DWARFDebugLine::getFileNameByIndex documentation:
// In Dwarf 4, the files are 1-indexed.
// In Dwarf 5, the files are 0-indexed.
// Additional discussions here:
// https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00883.html
// The ELF Reader is expecting the files are 1-indexed, so using
// the .debug_line header information decide if the indexed require
// an internal adjustment.
// For the case of GCC (DWARF5), if the entries[0] and [1] are the
// same, do not perform any adjustment.
auto DeduceIncrementFileIndex = [&]() -> bool {
if (CU->getVersion() < 5)
// DWARF-4 or earlier -> Don't increment index.
return false;
if (const DWARFDebugLine::LineTable *LT =
CU->getContext().getLineTableForUnit(CU.get())) {
// Check if there are at least 2 entries and if they are the same.
if (LT->hasFileAtIndex(0) && LT->hasFileAtIndex(1)) {
const DWARFDebugLine::FileNameEntry &EntryZero =
LT->Prologue.getFileNameEntry(0);
const DWARFDebugLine::FileNameEntry &EntryOne =
LT->Prologue.getFileNameEntry(1);
// Check directory indexes.
if (EntryZero.DirIdx != EntryOne.DirIdx)
// DWARF-5 -> Increment index.
return true;
// Check filename.
std::string FileZero;
std::string FileOne;
StringRef None;
LT->getFileNameByIndex(
0, None, DILineInfoSpecifier::FileLineInfoKind::RawValue,
FileZero);
LT->getFileNameByIndex(
1, None, DILineInfoSpecifier::FileLineInfoKind::RawValue,
FileOne);
return FileZero.compare(FileOne);
}
}
// DWARF-5 -> Increment index.
return true;
};
// The ELF reader expects the indexes as 1-indexed.
IncrementFileIndex = DeduceIncrementFileIndex();
DWARFDie UnitDie = CU->getUnitDIE();
SmallString<16> DWOAlternativeLocation;
if (UnitDie) {
std::optional<const char *> DWOFileName =
CU->getVersion() >= 5
? dwarf::toString(UnitDie.find(dwarf::DW_AT_dwo_name))
: dwarf::toString(UnitDie.find(dwarf::DW_AT_GNU_dwo_name));
StringRef From(DWOFileName.value_or(""));
DWOAlternativeLocation = createAlternativePath(From);
}
// The current CU can be a normal compile unit (standard) or a skeleton
// compile unit (split). For both cases, the returned die, will be used
// to create the logical scopes.
DWARFDie CUDie = CU->getNonSkeletonUnitDIE(
/*ExtractUnitDIEOnly=*/false,
/*DWOAlternativeLocation=*/DWOAlternativeLocation);
if (!CUDie.isValid())
continue;
// The current unit corresponds to the .dwo file. We need to get the
// skeleton unit and query for any ranges that will enclose any ranges
// in the non-skeleton unit.
DWARFDie DummyDie;
DWARFDie SkeletonDie =
CUDie.getDwarfUnit()->isDWOUnit() ? CU->getUnitDIE(false) : DummyDie;
// Disable the ranges processing if we have just a single .dwo object,
// as any DW_AT_ranges will access not available range information.
RangesDataAvailable =
(!CUDie.getDwarfUnit()->isDWOUnit() ||
(SkeletonDie.isValid() ? !SkeletonDie.getDwarfUnit()->isDWOUnit()
: true));
traverseDieAndChildren(CUDie, Root, SkeletonDie);
createLineAndFileRecords(DwarfContext->getLineTableForUnit(CU.get()));
if (Error Err = createInstructions())
return Err;
// Process the compilation unit, as there are cases where enclosed
// functions have the same ranges values. Insert the compilation unit
// ranges at the end, to allow enclosing ranges to be first in the list.
LVSectionIndex SectionIndex = getSectionIndex(CompileUnit);
addSectionRange(SectionIndex, CompileUnit);
LVRange *ScopesWithRanges = getSectionRanges(SectionIndex);
ScopesWithRanges->sort();
processLines(&CULines, SectionIndex);
processLocationGaps();
// These are per compile unit.
ScopesWithRanges->clear();
SymbolsWithLocations.clear();
CULines.clear();
}
return Error::success();
}
// Get the location information for the associated attribute.
void LVELFReader::processLocationList(dwarf::Attribute Attr,
const DWARFFormValue &FormValue,
const DWARFDie &Die,
uint64_t OffsetOnEntry,
bool CallSiteLocation) {
auto ProcessLocationExpression = [&](const DWARFExpression &Expression) {
// DW_OP_const_type is variable-length and has 3
// operands. DWARFExpression thus far only supports 2.
uint64_t Operands[2] = {0};
for (const DWARFExpression::Operation &Op : Expression) {
DWARFExpression::Operation::Description Description = Op.getDescription();
for (unsigned Operand = 0; Operand < 2; ++Operand) {
if (Description.Op[Operand] == DWARFExpression::Operation::SizeNA)
break;
Operands[Operand] = Op.getRawOperand(Operand);
}
CurrentSymbol->addLocationOperands(Op.getCode(), Operands[0],
Operands[1]);
}
};
DWARFUnit *U = Die.getDwarfUnit();
DWARFContext &DwarfContext = U->getContext();
bool IsLittleEndian = DwarfContext.isLittleEndian();
if (FormValue.isFormClass(DWARFFormValue::FC_Block) ||
(DWARFAttribute::mayHaveLocationExpr(Attr) &&
FormValue.isFormClass(DWARFFormValue::FC_Exprloc))) {
ArrayRef<uint8_t> Expr = *FormValue.getAsBlock();
DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()),
IsLittleEndian, 0);
DWARFExpression Expression(Data, U->getAddressByteSize(),
U->getFormParams().Format);
// Add location and operation entries.
CurrentSymbol->addLocation(Attr, /*LowPC=*/0, /*HighPC=*/-1,
/*SectionOffset=*/0, OffsetOnEntry,
CallSiteLocation);
ProcessLocationExpression(Expression);
return;
}
if (DWARFAttribute::mayHaveLocationList(Attr) &&
FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) {
uint64_t Offset = *FormValue.getAsSectionOffset();
if (FormValue.getForm() == dwarf::DW_FORM_loclistx) {
std::optional<uint64_t> LoclistOffset = U->getLoclistOffset(Offset);
if (!LoclistOffset)
return;
Offset = *LoclistOffset;
}
uint64_t BaseAddr = 0;
if (std::optional<SectionedAddress> BA = U->getBaseAddress())
BaseAddr = BA->Address;
LVAddress LowPC = 0;
LVAddress HighPC = 0;
auto ProcessLocationEntry = [&](const DWARFLocationEntry &Entry) {
if (Entry.Kind == dwarf::DW_LLE_base_address) {
BaseAddr = Entry.Value0;
return;
}
if (Entry.Kind == dwarf::DW_LLE_offset_pair) {
LowPC = BaseAddr + Entry.Value0;
HighPC = BaseAddr + Entry.Value1;
DWARFAddressRange Range{LowPC, HighPC, Entry.SectionIndex};
if (Range.SectionIndex == SectionedAddress::UndefSection)
Range.SectionIndex = Entry.SectionIndex;
DWARFLocationExpression Loc{Range, Entry.Loc};
DWARFDataExtractor Data(Loc.Expr, IsLittleEndian,
U->getAddressByteSize());
DWARFExpression Expression(Data, U->getAddressByteSize());
// Store the real upper limit for the address range.
if (UpdateHighAddress && HighPC > 0)
--HighPC;
// Add location and operation entries.
CurrentSymbol->addLocation(Attr, LowPC, HighPC, Offset, OffsetOnEntry,
CallSiteLocation);
ProcessLocationExpression(Expression);
}
};
Error E = U->getLocationTable().visitLocationList(
&Offset, [&](const DWARFLocationEntry &E) {
ProcessLocationEntry(E);
return true;
});
if (E)
consumeError(std::move(E));
}
}
void LVELFReader::processLocationMember(dwarf::Attribute Attr,
const DWARFFormValue &FormValue,
const DWARFDie &Die,
uint64_t OffsetOnEntry) {
// Check if the value is an integer constant.
if (FormValue.isFormClass(DWARFFormValue::FC_Constant))
// Add a record to hold a constant as location.
CurrentSymbol->addLocationConstant(Attr, *FormValue.getAsUnsignedConstant(),
OffsetOnEntry);
else
// This is a a location description, or a reference to one.
processLocationList(Attr, FormValue, Die, OffsetOnEntry);
}
// Update the current element with the reference.
void LVELFReader::updateReference(dwarf::Attribute Attr,
const DWARFFormValue &FormValue) {
// We are assuming that DW_AT_specification, DW_AT_abstract_origin,
// DW_AT_type and DW_AT_extension do not appear at the same time
// in the same DIE.
uint64_t Reference = *FormValue.getAsReference();
// Get target for the given reference, if already created.
LVElement *Target = getElementForOffset(Reference, CurrentElement);
// Check if we are dealing with cross CU references.
if (FormValue.getForm() == dwarf::DW_FORM_ref_addr) {
if (Target) {
// The global reference is ready. Mark it as global.
Target->setIsGlobalReference();
// Remove global reference from the unseen list.
removeGlobalOffset(Reference);
} else
// Record the unseen cross CU reference.
addGlobalOffset(Reference);
}
// At this point, 'Target' can be null, in the case of the target element
// not being seen. But the correct bit is set, to indicate that the target
// is being referenced by (abstract_origin, extension, specification) or
// (import, type).
// We must differentiate between the kind of reference. This is needed to
// complete inlined function instances with dropped abstract references,
// in order to facilitate a logical comparison.
switch (Attr) {
case dwarf::DW_AT_abstract_origin:
case dwarf::DW_AT_call_origin:
CurrentElement->setReference(Target);
CurrentElement->setHasReferenceAbstract();
break;
case dwarf::DW_AT_extension:
CurrentElement->setReference(Target);
CurrentElement->setHasReferenceExtension();
break;
case dwarf::DW_AT_specification:
CurrentElement->setReference(Target);
CurrentElement->setHasReferenceSpecification();
break;
case dwarf::DW_AT_import:
case dwarf::DW_AT_type:
CurrentElement->setType(Target);
break;
default:
break;
}
}
// Get an element given the DIE offset.
LVElement *LVELFReader::getElementForOffset(LVOffset Offset,
LVElement *Element) {
LVElement *Target = nullptr;
// Search offset in the cross references.
LVElementReference::iterator Iter = ElementTable.find(Offset);
if (Iter == ElementTable.end())
// Reference to an unseen element.
ElementTable.emplace(std::piecewise_construct,
std::forward_as_tuple(Offset),
std::forward_as_tuple(nullptr, LVElementSet{Element}));
else {
// There are previous references to this element. We need to update the
// element and all the references pointing to this element.
LVElementEntry &Reference = Iter->second;
Target = Reference.first;
if (!Target)
// Add the element to the set.
Reference.second.insert(Element);
}
return Target;
}
Error LVELFReader::loadTargetInfo(const ObjectFile &Obj) {
// Detect the architecture from the object file. We usually don't need OS
// info to lookup a target and create register info.
Triple TT;
TT.setArch(Triple::ArchType(Obj.getArch()));
TT.setVendor(Triple::UnknownVendor);
TT.setOS(Triple::UnknownOS);
// Features to be passed to target/subtarget
Expected<SubtargetFeatures> Features = Obj.getFeatures();
SubtargetFeatures FeaturesValue;
if (!Features) {
consumeError(Features.takeError());
FeaturesValue = SubtargetFeatures();
}
FeaturesValue = *Features;
return loadGenericTargetInfo(TT.str(), FeaturesValue.getString());
}
void LVELFReader::mapRangeAddress(const ObjectFile &Obj) {
for (auto Iter = Obj.symbol_begin(); Iter != Obj.symbol_end(); ++Iter) {
const SymbolRef &Symbol = *Iter;
Expected<SymbolRef::Type> TypeOrErr = Symbol.getType();
if (!TypeOrErr) {
consumeError(TypeOrErr.takeError());
continue;
}
// Process only symbols that represent a function.
SymbolRef::Type Type = *TypeOrErr;
if (Type != SymbolRef::ST_Function)
continue;
// In the case of a Mach-O STAB symbol, get its section only if
// the STAB symbol's section field refers to a valid section index.
// Otherwise the symbol may error trying to load a section that
// does not exist.
const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(&Obj);
bool IsSTAB = false;
if (MachO) {
DataRefImpl SymDRI = Symbol.getRawDataRefImpl();
uint8_t NType =
(MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type
: MachO->getSymbolTableEntry(SymDRI).n_type);
if (NType & MachO::N_STAB)
IsSTAB = true;
}
Expected<section_iterator> IterOrErr = Symbol.getSection();
if (!IterOrErr) {
consumeError(IterOrErr.takeError());
continue;
}
section_iterator Section = IsSTAB ? Obj.section_end() : *IterOrErr;
if (Section == Obj.section_end())
continue;
// Get the symbol value.
Expected<uint64_t> AddressOrErr = Symbol.getAddress();
if (!AddressOrErr) {
consumeError(AddressOrErr.takeError());
continue;
}
uint64_t Address = *AddressOrErr;
// Get symbol name.
StringRef Name;
Expected<StringRef> NameOrErr = Symbol.getName();
if (!NameOrErr) {
consumeError(NameOrErr.takeError());
continue;
}
Name = *NameOrErr;
// Check if the symbol is Comdat.
Expected<uint32_t> FlagsOrErr = Symbol.getFlags();
if (!FlagsOrErr) {
consumeError(FlagsOrErr.takeError());
continue;
}
uint32_t Flags = *FlagsOrErr;
// Mark the symbol as 'comdat' in any of the following cases:
// - Symbol has the SF_Weak flag or
// - Symbol section index different from the DotTextSectionIndex.
LVSectionIndex SectionIndex = Section->getIndex();
bool IsComdat =
(Flags & SymbolRef::SF_Weak) || (SectionIndex != DotTextSectionIndex);
// Record the symbol name (linkage) and its loading address.
addToSymbolTable(Name, Address, SectionIndex, IsComdat);
}
}
void LVELFReader::sortScopes() { Root->sort(); }
void LVELFReader::print(raw_ostream &OS) const {
OS << "LVType\n";
LLVM_DEBUG(dbgs() << "CreateReaders\n");
}