blob: c4b0b251c1201fcf9f27454b3b26fc861d5235bb [file] [log] [blame]
//===- bolt/Core/DIEBuilder.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
//
//===----------------------------------------------------------------------===//
#include "bolt/Core/DIEBuilder.h"
#include "bolt/Core/BinaryContext.h"
#include "bolt/Core/ParallelUtilities.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
#include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt"
namespace opts {
extern cl::opt<unsigned> Verbosity;
}
namespace llvm {
namespace bolt {
void DIEBuilder::updateReferences() {
for (auto &[SrcDIEInfo, ReferenceInfo] : getState().AddrReferences) {
DIEInfo *DstDIEInfo = ReferenceInfo.Dst;
DWARFUnitInfo &DstUnitInfo = getUnitInfo(DstDIEInfo->UnitId);
dwarf::Attribute Attr = ReferenceInfo.AttrSpec.Attr;
dwarf::Form Form = ReferenceInfo.AttrSpec.Form;
const uint64_t NewAddr =
DstDIEInfo->Die->getOffset() + DstUnitInfo.UnitOffset;
SrcDIEInfo->Die->replaceValue(getState().DIEAlloc, Attr, Form,
DIEInteger(NewAddr));
}
// Handling referenes in location expressions.
for (LocWithReference &LocExpr : getState().LocWithReferencesToProcess) {
SmallVector<uint8_t, 32> Buffer;
DataExtractor Data(StringRef((const char *)LocExpr.BlockData.data(),
LocExpr.BlockData.size()),
LocExpr.U.isLittleEndian(),
LocExpr.U.getAddressByteSize());
DWARFExpression Expr(Data, LocExpr.U.getAddressByteSize(),
LocExpr.U.getFormParams().Format);
cloneExpression(Data, Expr, LocExpr.U, Buffer, CloneExpressionStage::PATCH);
DIEValueList *AttrVal;
if (LocExpr.Form == dwarf::DW_FORM_exprloc) {
DIELoc *DL = new (getState().DIEAlloc) DIELoc;
DL->setSize(Buffer.size());
AttrVal = static_cast<DIEValueList *>(DL);
} else {
DIEBlock *DBL = new (getState().DIEAlloc) DIEBlock;
DBL->setSize(Buffer.size());
AttrVal = static_cast<DIEValueList *>(DBL);
}
for (auto Byte : Buffer)
AttrVal->addValue(getState().DIEAlloc, static_cast<dwarf::Attribute>(0),
dwarf::DW_FORM_data1, DIEInteger(Byte));
DIEValue Value;
if (LocExpr.Form == dwarf::DW_FORM_exprloc)
Value =
DIEValue(dwarf::Attribute(LocExpr.Attr), dwarf::Form(LocExpr.Form),
static_cast<DIELoc *>(AttrVal));
else
Value =
DIEValue(dwarf::Attribute(LocExpr.Attr), dwarf::Form(LocExpr.Form),
static_cast<DIEBlock *>(AttrVal));
LocExpr.Die.replaceValue(getState().DIEAlloc, LocExpr.Attr, LocExpr.Form,
Value);
}
return;
}
uint32_t DIEBuilder::allocDIE(const DWARFUnit &DU, const DWARFDie &DDie,
BumpPtrAllocator &Alloc, const uint32_t UId) {
DWARFUnitInfo &DWARFUnitInfo = getUnitInfo(UId);
const uint64_t DDieOffset = DDie.getOffset();
if (DWARFUnitInfo.DIEIDMap.count(DDieOffset))
return DWARFUnitInfo.DIEIDMap[DDieOffset];
DIE *Die = DIE::get(Alloc, dwarf::Tag(DDie.getTag()));
// This handles the case where there is a DIE ref which points to
// invalid DIE. This prevents assert when IR is written out.
// Also it makes debugging easier.
// DIE dump is not very useful.
// It's nice to know original offset from which this DIE was constructed.
Die->setOffset(DDie.getOffset());
if (opts::Verbosity >= 1)
getState().DWARFDieAddressesParsed.insert(DDie.getOffset());
const uint32_t DId = DWARFUnitInfo.DieInfoVector.size();
DWARFUnitInfo.DIEIDMap[DDieOffset] = DId;
DWARFUnitInfo.DieInfoVector.emplace_back(
std::make_unique<DIEInfo>(DIEInfo{Die, DId, UId}));
return DId;
}
void DIEBuilder::constructFromUnit(DWARFUnit &DU) {
std::optional<uint32_t> UnitId = getUnitId(DU);
if (!UnitId) {
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: "
<< "Skip Unit at " << Twine::utohexstr(DU.getOffset()) << "\n";
return;
}
const uint32_t UnitHeaderSize = DU.getHeaderSize();
uint64_t DIEOffset = DU.getOffset() + UnitHeaderSize;
uint64_t NextCUOffset = DU.getNextUnitOffset();
DWARFDataExtractor DebugInfoData = DU.getDebugInfoExtractor();
DWARFDebugInfoEntry DIEEntry;
std::vector<DIE *> CurParentDIEStack;
std::vector<uint32_t> Parents;
uint32_t TUTypeOffset = 0;
if (DWARFTypeUnit *TU = dyn_cast_or_null<DWARFTypeUnit>(&DU))
TUTypeOffset = TU->getTypeOffset();
assert(DebugInfoData.isValidOffset(NextCUOffset - 1));
Parents.push_back(UINT32_MAX);
do {
const bool IsTypeDIE = (TUTypeOffset == DIEOffset - DU.getOffset());
if (!DIEEntry.extractFast(DU, &DIEOffset, DebugInfoData, NextCUOffset,
Parents.back()))
break;
if (const DWARFAbbreviationDeclaration *AbbrDecl =
DIEEntry.getAbbreviationDeclarationPtr()) {
DWARFDie DDie(&DU, &DIEEntry);
DIE *CurDIE = constructDIEFast(DDie, DU, *UnitId);
DWARFUnitInfo &UI = getUnitInfo(*UnitId);
// Can't rely on first element in DieVector due to cross CU forward
// references.
if (!UI.UnitDie)
UI.UnitDie = CurDIE;
if (IsTypeDIE)
getState().TypeDIEMap[&DU] = CurDIE;
if (!CurParentDIEStack.empty())
CurParentDIEStack.back()->addChild(CurDIE);
if (AbbrDecl->hasChildren())
CurParentDIEStack.push_back(CurDIE);
} else {
// NULL DIE: finishes current children scope.
CurParentDIEStack.pop_back();
}
} while (CurParentDIEStack.size() > 0);
getState().CloneUnitCtxMap[*UnitId].IsConstructed = true;
}
DIEBuilder::DIEBuilder(BinaryContext &BC, DWARFContext *DwarfContext,
DWARF5AcceleratorTable &DebugNamesTable,
DWARFUnit *SkeletonCU)
: BC(BC), DwarfContext(DwarfContext), SkeletonCU(SkeletonCU),
DebugNamesTable(DebugNamesTable) {}
static unsigned int getCUNum(DWARFContext *DwarfContext, bool IsDWO) {
unsigned int CUNum = IsDWO ? DwarfContext->getNumDWOCompileUnits()
: DwarfContext->getNumCompileUnits();
CUNum += IsDWO ? DwarfContext->getNumDWOTypeUnits()
: DwarfContext->getNumTypeUnits();
return CUNum;
}
void DIEBuilder::buildTypeUnits(DebugStrOffsetsWriter *StrOffsetWriter,
const bool Init) {
if (Init)
BuilderState.reset(new State());
const DWARFUnitIndex &TUIndex = DwarfContext->getTUIndex();
if (!TUIndex.getRows().empty()) {
for (auto &Row : TUIndex.getRows()) {
uint64_t Signature = Row.getSignature();
// manually populate TypeUnit to UnitVector
DwarfContext->getTypeUnitForHash(DwarfContext->getMaxVersion(), Signature,
true);
}
}
const unsigned int CUNum = getCUNum(DwarfContext, isDWO());
getState().CloneUnitCtxMap.resize(CUNum);
DWARFContext::unit_iterator_range CU4TURanges =
isDWO() ? DwarfContext->dwo_types_section_units()
: DwarfContext->types_section_units();
getState().Type = ProcessingType::DWARF4TUs;
for (std::unique_ptr<DWARFUnit> &DU : CU4TURanges)
registerUnit(*DU.get(), false);
for (std::unique_ptr<DWARFUnit> &DU : CU4TURanges)
constructFromUnit(*DU.get());
DWARFContext::unit_iterator_range CURanges =
isDWO() ? DwarfContext->dwo_info_section_units()
: DwarfContext->info_section_units();
// This handles DWARF4 CUs and DWARF5 CU/TUs.
// Creating a vector so that for reference handling only DWARF5 CU/TUs are
// used, and not DWARF4 TUs.
getState().Type = ProcessingType::DWARF5TUs;
for (std::unique_ptr<DWARFUnit> &DU : CURanges) {
if (!DU->isTypeUnit())
continue;
registerUnit(*DU.get(), false);
}
for (DWARFUnit *DU : getState().DWARF5TUVector) {
constructFromUnit(*DU);
if (StrOffsetWriter)
StrOffsetWriter->finalizeSection(*DU, *this);
}
}
void DIEBuilder::buildCompileUnits(const bool Init) {
if (Init)
BuilderState.reset(new State());
unsigned int CUNum = getCUNum(DwarfContext, isDWO());
getState().CloneUnitCtxMap.resize(CUNum);
DWARFContext::unit_iterator_range CURanges =
isDWO() ? DwarfContext->dwo_info_section_units()
: DwarfContext->info_section_units();
// This handles DWARF4 CUs and DWARF5 CU/TUs.
// Creating a vector so that for reference handling only DWARF5 CU/TUs are
// used, and not DWARF4 TUs.getState().DUList
getState().Type = ProcessingType::CUs;
for (std::unique_ptr<DWARFUnit> &DU : CURanges) {
if (DU->isTypeUnit())
continue;
registerUnit(*DU.get(), false);
}
// Using DULIst since it can be modified by cross CU refrence resolution.
for (DWARFUnit *DU : getState().DUList) {
if (DU->isTypeUnit())
continue;
constructFromUnit(*DU);
}
}
void DIEBuilder::buildCompileUnits(const std::vector<DWARFUnit *> &CUs) {
BuilderState.reset(new State());
// Allocating enough for current batch being processed.
// In real use cases we either processing a batch of CUs with no cross
// references, or if they do have them it is due to LTO. With clang they will
// share the same abbrev table. In either case this vector will not grow.
getState().CloneUnitCtxMap.resize(CUs.size());
getState().Type = ProcessingType::CUs;
for (DWARFUnit *CU : CUs)
registerUnit(*CU, false);
for (DWARFUnit *DU : getState().DUList)
constructFromUnit(*DU);
}
void DIEBuilder::buildDWOUnit(DWARFUnit &U) {
BuilderState.release();
BuilderState = std::make_unique<State>();
buildTypeUnits(nullptr, false);
getState().Type = ProcessingType::CUs;
registerUnit(U, false);
constructFromUnit(U);
}
DIE *DIEBuilder::constructDIEFast(DWARFDie &DDie, DWARFUnit &U,
uint32_t UnitId) {
std::optional<uint32_t> Idx = getAllocDIEId(U, DDie);
if (Idx) {
DWARFUnitInfo &DWARFUnitInfo = getUnitInfo(UnitId);
DIEInfo &DieInfo = getDIEInfo(UnitId, *Idx);
if (DWARFUnitInfo.IsConstructed && DieInfo.Die)
return DieInfo.Die;
} else {
Idx = allocDIE(U, DDie, getState().DIEAlloc, UnitId);
}
DIEInfo &DieInfo = getDIEInfo(UnitId, *Idx);
uint64_t Offset = DDie.getOffset();
uint64_t NextOffset = Offset;
DWARFDataExtractor Data = U.getDebugInfoExtractor();
DWARFDebugInfoEntry DDIEntry;
if (DDIEntry.extractFast(U, &NextOffset, Data, U.getNextUnitOffset(), 0))
assert(NextOffset - U.getOffset() <= Data.getData().size() &&
"NextOffset OOB");
SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset));
Data =
DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize());
const DWARFAbbreviationDeclaration *Abbrev =
DDie.getAbbreviationDeclarationPtr();
uint64_t AttrOffset = getULEB128Size(Abbrev->getCode());
using AttrSpec = DWARFAbbreviationDeclaration::AttributeSpec;
for (const AttrSpec &AttrSpec : Abbrev->attributes()) {
DWARFFormValue Val(AttrSpec.Form);
Val.extractValue(Data, &AttrOffset, U.getFormParams(), &U);
cloneAttribute(*DieInfo.Die, DDie, U, Val, AttrSpec);
}
return DieInfo.Die;
}
static DWARFUnit *
getUnitForOffset(DIEBuilder &Builder, DWARFContext &DWCtx,
const uint64_t Offset,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec) {
auto findUnit = [&](std::vector<DWARFUnit *> &Units) -> DWARFUnit * {
auto CUIter = llvm::upper_bound(Units, Offset,
[](uint64_t LHS, const DWARFUnit *RHS) {
return LHS < RHS->getNextUnitOffset();
});
static std::vector<DWARFUnit *> CUOffsets;
static std::once_flag InitVectorFlag;
auto initCUVector = [&]() {
CUOffsets.reserve(DWCtx.getNumCompileUnits());
for (const std::unique_ptr<DWARFUnit> &CU : DWCtx.compile_units())
CUOffsets.emplace_back(CU.get());
};
DWARFUnit *CU = CUIter != Units.end() ? *CUIter : nullptr;
// Above algorithm breaks when there is only one CU, and reference is
// outside of it. Fall through slower path, that searches all the CUs.
// For example when src and destination of cross CU references have
// different abbrev section.
if (!CU ||
(CU && AttrSpec.Form == dwarf::DW_FORM_ref_addr &&
!(CU->getOffset() < Offset && CU->getNextUnitOffset() > Offset))) {
// This is a work around for XCode clang. There is a build error when we
// pass DWCtx.compile_units() to llvm::upper_bound
std::call_once(InitVectorFlag, initCUVector);
auto CUIter = std::upper_bound(CUOffsets.begin(), CUOffsets.end(), Offset,
[](uint64_t LHS, const DWARFUnit *RHS) {
return LHS < RHS->getNextUnitOffset();
});
CU = CUIter != CUOffsets.end() ? (*CUIter) : nullptr;
}
return CU;
};
switch (Builder.getCurrentProcessingState()) {
case DIEBuilder::ProcessingType::DWARF4TUs:
return findUnit(Builder.getDWARF4TUVector());
case DIEBuilder::ProcessingType::DWARF5TUs:
return findUnit(Builder.getDWARF5TUVector());
case DIEBuilder::ProcessingType::CUs:
return findUnit(Builder.getDWARFCUVector());
};
return nullptr;
}
uint32_t DIEBuilder::finalizeDIEs(
DWARFUnit &CU, DIE &Die,
std::vector<std::optional<BOLTDWARF5AccelTableData *>> &Parents,
uint32_t &CurOffset) {
getState().DWARFDieAddressesParsed.erase(Die.getOffset());
uint32_t CurSize = 0;
Die.setOffset(CurOffset);
std::optional<BOLTDWARF5AccelTableData *> NameEntry =
DebugNamesTable.addAccelTableEntry(
CU, Die, SkeletonCU ? SkeletonCU->getDWOId() : std::nullopt,
Parents.back());
// It is possible that an indexed debugging information entry has a parent
// that is not indexed (for example, if its parent does not have a name
// attribute). In such a case, a parent attribute may point to a nameless
// index entry (that is, one that cannot be reached from any entry in the name
// table), or it may point to the nearest ancestor that does have an index
// entry.
if (NameEntry)
Parents.push_back(std::move(NameEntry));
for (DIEValue &Val : Die.values())
CurSize += Val.sizeOf(CU.getFormParams());
CurSize += getULEB128Size(Die.getAbbrevNumber());
CurOffset += CurSize;
for (DIE &Child : Die.children()) {
uint32_t ChildSize = finalizeDIEs(CU, Child, Parents, CurOffset);
CurSize += ChildSize;
}
// for children end mark.
if (Die.hasChildren()) {
CurSize += sizeof(uint8_t);
CurOffset += sizeof(uint8_t);
}
Die.setSize(CurSize);
if (NameEntry)
Parents.pop_back();
return CurSize;
}
void DIEBuilder::finish() {
auto finalizeCU = [&](DWARFUnit &CU, uint64_t &UnitStartOffset) -> void {
DIE *UnitDIE = getUnitDIEbyUnit(CU);
uint32_t HeaderSize = CU.getHeaderSize();
uint32_t CurOffset = HeaderSize;
DebugNamesTable.setCurrentUnit(CU, UnitStartOffset);
std::vector<std::optional<BOLTDWARF5AccelTableData *>> Parents;
Parents.push_back(std::nullopt);
finalizeDIEs(CU, *UnitDIE, Parents, CurOffset);
DWARFUnitInfo &CurUnitInfo = getUnitInfoByDwarfUnit(CU);
CurUnitInfo.UnitOffset = UnitStartOffset;
CurUnitInfo.UnitLength = HeaderSize + UnitDIE->getSize();
UnitStartOffset += CurUnitInfo.UnitLength;
};
// Computing offsets for .debug_types section.
// It's processed first when CU is registered so will be at the begginnig of
// the vector.
uint64_t TypeUnitStartOffset = 0;
for (DWARFUnit *CU : getState().DUList) {
// We process DWARF$ types first.
if (!(CU->getVersion() < 5 && CU->isTypeUnit()))
break;
finalizeCU(*CU, TypeUnitStartOffset);
}
for (DWARFUnit *CU : getState().DUList) {
// Skipping DWARF4 types.
if (CU->getVersion() < 5 && CU->isTypeUnit())
continue;
finalizeCU(*CU, UnitSize);
}
if (opts::Verbosity >= 1) {
if (!getState().DWARFDieAddressesParsed.empty())
dbgs() << "Referenced DIE offsets not in .debug_info\n";
for (const uint64_t Address : getState().DWARFDieAddressesParsed) {
dbgs() << Twine::utohexstr(Address) << "\n";
}
}
updateReferences();
}
DWARFDie DIEBuilder::resolveDIEReference(
const DWARFFormValue &RefValue,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
DWARFUnit *&RefCU, DWARFDebugInfoEntry &DwarfDebugInfoEntry) {
assert(RefValue.isFormClass(DWARFFormValue::FC_Reference));
uint64_t RefOffset = *RefValue.getAsReference();
return resolveDIEReference(AttrSpec, RefOffset, RefCU, DwarfDebugInfoEntry);
}
DWARFDie DIEBuilder::resolveDIEReference(
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const uint64_t RefOffset, DWARFUnit *&RefCU,
DWARFDebugInfoEntry &DwarfDebugInfoEntry) {
uint64_t TmpRefOffset = RefOffset;
if ((RefCU =
getUnitForOffset(*this, *DwarfContext, TmpRefOffset, AttrSpec))) {
/// Trying to add to current working set in case it's cross CU reference.
registerUnit(*RefCU, true);
DWARFDataExtractor DebugInfoData = RefCU->getDebugInfoExtractor();
if (DwarfDebugInfoEntry.extractFast(*RefCU, &TmpRefOffset, DebugInfoData,
RefCU->getNextUnitOffset(), 0)) {
// In a file with broken references, an attribute might point to a NULL
// DIE.
DWARFDie RefDie = DWARFDie(RefCU, &DwarfDebugInfoEntry);
if (!RefDie.isNULL()) {
std::optional<uint32_t> UnitId = getUnitId(*RefCU);
// forward reference
if (UnitId && !getState().CloneUnitCtxMap[*UnitId].IsConstructed &&
!getAllocDIEId(*RefCU, RefDie))
allocDIE(*RefCU, RefDie, getState().DIEAlloc, *UnitId);
return RefDie;
}
BC.errs()
<< "BOLT-WARNING: [internal-dwarf-error]: invalid referenced DIE "
"at offset: "
<< Twine::utohexstr(RefOffset) << ".\n";
} else {
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: could not parse "
"referenced DIE at offset: "
<< Twine::utohexstr(RefOffset) << ".\n";
}
} else {
BC.errs()
<< "BOLT-WARNING: [internal-dwarf-error]: could not find referenced "
"CU. Referenced DIE offset: "
<< Twine::utohexstr(RefOffset) << ".\n";
}
return DWARFDie();
}
void DIEBuilder::cloneDieReferenceAttribute(
DIE &Die, const DWARFUnit &U, const DWARFDie &InputDIE,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
const uint64_t Ref = *Val.getAsReference();
DIE *NewRefDie = nullptr;
DWARFUnit *RefUnit = nullptr;
DWARFDebugInfoEntry DDIEntry;
const DWARFDie RefDie = resolveDIEReference(Val, AttrSpec, RefUnit, DDIEntry);
if (!RefDie)
return;
const std::optional<uint32_t> UnitId = getUnitId(*RefUnit);
const std::optional<uint32_t> IsAllocId = getAllocDIEId(*RefUnit, RefDie);
assert(IsAllocId.has_value() && "Encountered unexpected unallocated DIE.");
const uint32_t DIEId = *IsAllocId;
DIEInfo &DieInfo = getDIEInfo(*UnitId, DIEId);
if (!DieInfo.Die) {
assert(Ref > InputDIE.getOffset());
(void)Ref;
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: encounter unexpected "
"unallocated DIE. Should be alloc!\n";
// We haven't cloned this DIE yet. Just create an empty one and
// store it. It'll get really cloned when we process it.
DieInfo.Die = DIE::get(getState().DIEAlloc, dwarf::Tag(RefDie.getTag()));
}
NewRefDie = DieInfo.Die;
if (AttrSpec.Form == dwarf::DW_FORM_ref_addr) {
// Adding referenced DIE to DebugNames to be used when entries are created
// that contain cross cu references.
if (DebugNamesTable.canGenerateEntryWithCrossCUReference(U, Die, AttrSpec))
DebugNamesTable.addCrossCUDie(DieInfo.Die);
// no matter forward reference or backward reference, we are supposed
// to calculate them in `finish` due to the possible modification of
// the DIE.
DWARFDie CurDie = const_cast<DWARFDie &>(InputDIE);
DIEInfo *CurDieInfo = &getDIEInfoByDwarfDie(CurDie);
getState().AddrReferences.push_back(
std::make_pair(CurDieInfo, AddrReferenceInfo(&DieInfo, AttrSpec)));
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, dwarf::DW_FORM_ref_addr,
DIEInteger(DieInfo.Die->getOffset()));
return;
}
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, AttrSpec.Form,
DIEEntry(*NewRefDie));
}
void DIEBuilder::cloneStringAttribute(
DIE &Die, const DWARFUnit &U,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
if (AttrSpec.Form == dwarf::DW_FORM_string) {
Expected<const char *> StrAddr = Val.getAsCString();
if (!StrAddr) {
consumeError(StrAddr.takeError());
return;
}
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, dwarf::DW_FORM_string,
new (getState().DIEAlloc)
DIEInlineString(StrAddr.get(), getState().DIEAlloc));
} else {
std::optional<uint64_t> OffsetIndex = Val.getRawUValue();
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, AttrSpec.Form,
DIEInteger(*OffsetIndex));
}
}
bool DIEBuilder::cloneExpression(const DataExtractor &Data,
const DWARFExpression &Expression,
DWARFUnit &U,
SmallVectorImpl<uint8_t> &OutputBuffer,
const CloneExpressionStage &Stage) {
using Encoding = DWARFExpression::Operation::Encoding;
using Descr = DWARFExpression::Operation::Description;
uint64_t OpOffset = 0;
bool DoesContainReference = false;
for (const DWARFExpression::Operation &Op : Expression) {
const Descr &Description = Op.getDescription();
// DW_OP_const_type is variable-length and has 3
// operands. Thus far we only support 2.
if ((Description.Op.size() == 2 &&
Description.Op[0] == Encoding::BaseTypeRef) ||
(Description.Op.size() == 2 &&
Description.Op[1] == Encoding::BaseTypeRef &&
Description.Op[0] != Encoding::Size1))
BC.outs() << "BOLT-WARNING: [internal-dwarf-error]: unsupported DW_OP "
"encoding.\n";
if ((Description.Op.size() == 1 &&
Description.Op[0] == Encoding::BaseTypeRef) ||
(Description.Op.size() == 2 &&
Description.Op[1] == Encoding::BaseTypeRef &&
Description.Op[0] == Encoding::Size1)) {
// This code assumes that the other non-typeref operand fits into 1
// byte.
assert(OpOffset < Op.getEndOffset());
const uint32_t ULEBsize = Op.getEndOffset() - OpOffset - 1;
(void)ULEBsize;
assert(ULEBsize <= 16);
// Copy over the operation.
OutputBuffer.push_back(Op.getCode());
uint64_t RefOffset;
if (Description.Op.size() == 1) {
RefOffset = Op.getRawOperand(0);
} else {
OutputBuffer.push_back(Op.getRawOperand(0));
RefOffset = Op.getRawOperand(1);
}
uint32_t Offset = 0;
if (RefOffset > 0 || Op.getCode() != dwarf::DW_OP_convert) {
DoesContainReference = true;
std::optional<uint32_t> RefDieID =
getAllocDIEId(U, U.getOffset() + RefOffset);
std::optional<uint32_t> RefUnitID = getUnitId(U);
if (RefDieID.has_value() && RefUnitID.has_value()) {
DIEInfo &RefDieInfo = getDIEInfo(*RefUnitID, *RefDieID);
if (DIE *Clone = RefDieInfo.Die)
Offset = Stage == CloneExpressionStage::INIT ? RefOffset
: Clone->getOffset();
else
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: base type ref "
"doesn't point to "
"DW_TAG_base_type.\n";
}
}
uint8_t ULEB[16];
// Hard coding to max size so size doesn't change when we update the
// offset.
encodeULEB128(Offset, ULEB, 4);
ArrayRef<uint8_t> ULEBbytes(ULEB, 4);
OutputBuffer.append(ULEBbytes.begin(), ULEBbytes.end());
} else {
// Copy over everything else unmodified.
const StringRef Bytes = Data.getData().slice(OpOffset, Op.getEndOffset());
OutputBuffer.append(Bytes.begin(), Bytes.end());
}
OpOffset = Op.getEndOffset();
}
return DoesContainReference;
}
void DIEBuilder::cloneBlockAttribute(
DIE &Die, DWARFUnit &U,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
DIEValueList *Attr;
DIEValue Value;
DIELoc *Loc = nullptr;
DIEBlock *Block = nullptr;
if (AttrSpec.Form == dwarf::DW_FORM_exprloc) {
Loc = new (getState().DIEAlloc) DIELoc;
} else if (doesFormBelongToClass(AttrSpec.Form, DWARFFormValue::FC_Block,
U.getVersion())) {
Block = new (getState().DIEAlloc) DIEBlock;
} else {
BC.errs()
<< "BOLT-WARNING: [internal-dwarf-error]: Unexpected Form value in "
"cloneBlockAttribute\n";
return;
}
Attr = Loc ? static_cast<DIEValueList *>(Loc)
: static_cast<DIEValueList *>(Block);
SmallVector<uint8_t, 32> Buffer;
ArrayRef<uint8_t> Bytes = *Val.getAsBlock();
if (DWARFAttribute::mayHaveLocationExpr(AttrSpec.Attr) &&
(Val.isFormClass(DWARFFormValue::FC_Block) ||
Val.isFormClass(DWARFFormValue::FC_Exprloc))) {
DataExtractor Data(StringRef((const char *)Bytes.data(), Bytes.size()),
U.isLittleEndian(), U.getAddressByteSize());
DWARFExpression Expr(Data, U.getAddressByteSize(),
U.getFormParams().Format);
if (cloneExpression(Data, Expr, U, Buffer, CloneExpressionStage::INIT))
getState().LocWithReferencesToProcess.emplace_back(
Bytes.vec(), U, Die, AttrSpec.Form, AttrSpec.Attr);
Bytes = Buffer;
}
for (auto Byte : Bytes)
Attr->addValue(getState().DIEAlloc, static_cast<dwarf::Attribute>(0),
dwarf::DW_FORM_data1, DIEInteger(Byte));
if (Loc)
Loc->setSize(Bytes.size());
else
Block->setSize(Bytes.size());
if (Loc)
Value = DIEValue(dwarf::Attribute(AttrSpec.Attr),
dwarf::Form(AttrSpec.Form), Loc);
else
Value = DIEValue(dwarf::Attribute(AttrSpec.Attr),
dwarf::Form(AttrSpec.Form), Block);
Die.addValue(getState().DIEAlloc, Value);
}
void DIEBuilder::cloneAddressAttribute(
DIE &Die, const DWARFUnit &U,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, AttrSpec.Form,
DIEInteger(Val.getRawUValue()));
}
void DIEBuilder::cloneRefsigAttribute(
DIE &Die, DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
const std::optional<uint64_t> SigVal = Val.getRawUValue();
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, dwarf::DW_FORM_ref_sig8,
DIEInteger(*SigVal));
}
void DIEBuilder::cloneScalarAttribute(
DIE &Die, const DWARFDie &InputDIE,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
uint64_t Value;
if (auto OptionalValue = Val.getAsUnsignedConstant())
Value = *OptionalValue;
else if (auto OptionalValue = Val.getAsSignedConstant())
Value = *OptionalValue;
else if (auto OptionalValue = Val.getAsSectionOffset())
Value = *OptionalValue;
else {
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: Unsupported scalar "
"attribute form. Dropping "
"attribute.\n";
return;
}
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, AttrSpec.Form,
DIEInteger(Value));
}
void DIEBuilder::cloneLoclistAttrubute(
DIE &Die, const DWARFDie &InputDIE,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
const DWARFFormValue &Val) {
std::optional<uint64_t> Value = std::nullopt;
if (auto OptionalValue = Val.getAsUnsignedConstant())
Value = OptionalValue;
else if (auto OptionalValue = Val.getAsSignedConstant())
Value = OptionalValue;
else if (auto OptionalValue = Val.getAsSectionOffset())
Value = OptionalValue;
else
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: Unsupported scalar "
"attribute form. Dropping "
"attribute.\n";
if (!Value.has_value())
return;
Die.addValue(getState().DIEAlloc, AttrSpec.Attr, AttrSpec.Form,
DIELocList(*Value));
}
void DIEBuilder::cloneAttribute(
DIE &Die, const DWARFDie &InputDIE, DWARFUnit &U, const DWARFFormValue &Val,
const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec) {
switch (AttrSpec.Form) {
case dwarf::DW_FORM_strp:
case dwarf::DW_FORM_string:
case dwarf::DW_FORM_strx:
case dwarf::DW_FORM_strx1:
case dwarf::DW_FORM_strx2:
case dwarf::DW_FORM_strx3:
case dwarf::DW_FORM_strx4:
case dwarf::DW_FORM_GNU_str_index:
case dwarf::DW_FORM_line_strp:
cloneStringAttribute(Die, U, AttrSpec, Val);
break;
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:
cloneDieReferenceAttribute(Die, U, InputDIE, AttrSpec, Val);
break;
case dwarf::DW_FORM_block:
case dwarf::DW_FORM_block1:
case dwarf::DW_FORM_block2:
case dwarf::DW_FORM_block4:
case dwarf::DW_FORM_exprloc:
cloneBlockAttribute(Die, U, AttrSpec, Val);
break;
case dwarf::DW_FORM_addr:
case dwarf::DW_FORM_addrx:
case dwarf::DW_FORM_GNU_addr_index:
cloneAddressAttribute(Die, U, AttrSpec, Val);
break;
case dwarf::DW_FORM_data1:
case dwarf::DW_FORM_data2:
case dwarf::DW_FORM_data4:
case dwarf::DW_FORM_data8:
case dwarf::DW_FORM_udata:
case dwarf::DW_FORM_sdata:
case dwarf::DW_FORM_sec_offset:
case dwarf::DW_FORM_rnglistx:
case dwarf::DW_FORM_flag:
case dwarf::DW_FORM_flag_present:
case dwarf::DW_FORM_implicit_const:
cloneScalarAttribute(Die, InputDIE, AttrSpec, Val);
break;
case dwarf::DW_FORM_loclistx:
cloneLoclistAttrubute(Die, InputDIE, AttrSpec, Val);
break;
case dwarf::DW_FORM_ref_sig8:
cloneRefsigAttribute(Die, AttrSpec, Val);
break;
default:
BC.errs() << "BOLT-WARNING: [internal-dwarf-error]: Unsupported attribute "
"form " +
dwarf::FormEncodingString(AttrSpec.Form).str() +
" in cloneAttribute. Dropping.";
}
}
void DIEBuilder::assignAbbrev(DIEAbbrev &Abbrev) {
// Check the set for priors.
FoldingSetNodeID ID;
Abbrev.Profile(ID);
void *InsertToken;
DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken);
// If it's newly added.
if (InSet) {
// Assign existing abbreviation number.
Abbrev.setNumber(InSet->getNumber());
} else {
// Add to abbreviation list.
Abbreviations.push_back(
std::make_unique<DIEAbbrev>(Abbrev.getTag(), Abbrev.hasChildren()));
for (const auto &Attr : Abbrev.getData())
Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm());
AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken);
// Assign the unique abbreviation number.
Abbrev.setNumber(Abbreviations.size());
Abbreviations.back()->setNumber(Abbreviations.size());
}
}
void DIEBuilder::generateAbbrevs() {
if (isEmpty())
return;
for (DWARFUnit *DU : getState().DUList) {
DIE *UnitDIE = getUnitDIEbyUnit(*DU);
generateUnitAbbrevs(UnitDIE);
}
}
void DIEBuilder::generateUnitAbbrevs(DIE *Die) {
DIEAbbrev NewAbbrev = Die->generateAbbrev();
if (Die->hasChildren())
NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes);
assignAbbrev(NewAbbrev);
Die->setAbbrevNumber(NewAbbrev.getNumber());
for (auto &Child : Die->children()) {
generateUnitAbbrevs(&Child);
}
}
static uint64_t getHash(const DWARFUnit &DU) {
// Before DWARF5 TU units are in their own section, so at least one offset,
// first one, will be the same as CUs in .debug_info.dwo section
if (DU.getVersion() < 5 && DU.isTypeUnit()) {
const uint64_t TypeUnitHash =
cast_or_null<DWARFTypeUnit>(&DU)->getTypeHash();
const uint64_t Offset = DU.getOffset();
return llvm::hash_combine(llvm::hash_value(TypeUnitHash),
llvm::hash_value(Offset));
}
return DU.getOffset();
}
void DIEBuilder::registerUnit(DWARFUnit &DU, bool NeedSort) {
auto IterGlobal = AllProcessed.insert(getHash(DU));
// If DU is already in a current working set or was already processed we can
// skip it.
if (!IterGlobal.second)
return;
if (getState().Type == ProcessingType::DWARF4TUs) {
getState().DWARF4TUVector.push_back(&DU);
} else if (getState().Type == ProcessingType::DWARF5TUs) {
getState().DWARF5TUVector.push_back(&DU);
} else {
getState().DWARFCUVector.push_back(&DU);
/// Sorting for cross CU reference resolution.
if (NeedSort)
std::sort(getState().DWARFCUVector.begin(),
getState().DWARFCUVector.end(),
[](const DWARFUnit *A, const DWARFUnit *B) {
return A->getOffset() < B->getOffset();
});
}
getState().UnitIDMap[getHash(DU)] = getState().DUList.size();
// This handles the case where we do have cross cu references, but CUs do not
// share the same abbrev table.
if (getState().DUList.size() == getState().CloneUnitCtxMap.size())
getState().CloneUnitCtxMap.emplace_back();
getState().DUList.push_back(&DU);
}
std::optional<uint32_t> DIEBuilder::getUnitId(const DWARFUnit &DU) {
auto Iter = getState().UnitIDMap.find(getHash(DU));
if (Iter != getState().UnitIDMap.end())
return Iter->second;
return std::nullopt;
}
} // namespace bolt
} // namespace llvm