forked from OSchip/llvm-project
1584 lines
62 KiB
C++
1584 lines
62 KiB
C++
//===- bolt/Rewrite/DWARFRewriter.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/Rewrite/DWARFRewriter.h"
|
|
#include "bolt/Core/BinaryContext.h"
|
|
#include "bolt/Core/BinaryFunction.h"
|
|
#include "bolt/Core/DebugData.h"
|
|
#include "bolt/Core/ParallelUtilities.h"
|
|
#include "bolt/Rewrite/RewriteInstance.h"
|
|
#include "bolt/Utils/Utils.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DWP/DWP.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
|
|
#include "llvm/MC/MCAsmBackend.h"
|
|
#include "llvm/MC/MCAsmLayout.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/Endian.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/ThreadPool.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#undef DEBUG_TYPE
|
|
#define DEBUG_TYPE "bolt"
|
|
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
static void printDie(const DWARFDie &DIE) {
|
|
DIDumpOptions DumpOpts;
|
|
DumpOpts.ShowForm = true;
|
|
DumpOpts.Verbose = true;
|
|
DumpOpts.ChildRecurseDepth = 0;
|
|
DumpOpts.ShowChildren = 0;
|
|
DIE.dump(dbgs(), 0, DumpOpts);
|
|
}
|
|
|
|
struct AttrInfo {
|
|
DWARFFormValue V;
|
|
uint64_t Offset;
|
|
uint32_t Size; // Size of the attribute.
|
|
};
|
|
|
|
/// Finds attributes FormValue and Offset.
|
|
///
|
|
/// \param DIE die to look up in.
|
|
/// \param Index the attribute index to extract.
|
|
/// \return an optional AttrInfo with DWARFFormValue and Offset.
|
|
static Optional<AttrInfo>
|
|
findAttributeInfo(const DWARFDie DIE,
|
|
const DWARFAbbreviationDeclaration *AbbrevDecl,
|
|
uint32_t Index) {
|
|
const DWARFUnit &U = *DIE.getDwarfUnit();
|
|
uint64_t Offset =
|
|
AbbrevDecl->getAttributeOffsetFromIndex(Index, DIE.getOffset(), U);
|
|
Optional<DWARFFormValue> Value =
|
|
AbbrevDecl->getAttributeValueFromOffset(Index, Offset, U);
|
|
if (!Value)
|
|
return None;
|
|
// AttributeSpec
|
|
const DWARFAbbreviationDeclaration::AttributeSpec *AttrVal =
|
|
AbbrevDecl->attributes().begin() + Index;
|
|
uint32_t ValSize = 0;
|
|
Optional<int64_t> ValSizeOpt = AttrVal->getByteSize(U);
|
|
if (ValSizeOpt) {
|
|
ValSize = static_cast<uint32_t>(*ValSizeOpt);
|
|
} else {
|
|
DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor();
|
|
uint64_t NewOffset = Offset;
|
|
DWARFFormValue::skipValue(Value->getForm(), DebugInfoData, &NewOffset,
|
|
U.getFormParams());
|
|
// This includes entire size of the entry, which might not be just the
|
|
// encoding part. For example for DW_AT_loc it will include expression
|
|
// location.
|
|
ValSize = NewOffset - Offset;
|
|
}
|
|
|
|
return AttrInfo{*Value, Offset, ValSize};
|
|
}
|
|
|
|
/// Finds attributes FormValue and Offset.
|
|
///
|
|
/// \param DIE die to look up in.
|
|
/// \param Attr the attribute to extract.
|
|
/// \return an optional AttrInfo with DWARFFormValue and Offset.
|
|
static Optional<AttrInfo> findAttributeInfo(const DWARFDie DIE,
|
|
dwarf::Attribute Attr) {
|
|
if (!DIE.isValid())
|
|
return None;
|
|
const DWARFAbbreviationDeclaration *AbbrevDecl =
|
|
DIE.getAbbreviationDeclarationPtr();
|
|
if (!AbbrevDecl)
|
|
return None;
|
|
Optional<uint32_t> Index = AbbrevDecl->findAttributeIndex(Attr);
|
|
if (!Index)
|
|
return None;
|
|
return findAttributeInfo(DIE, AbbrevDecl, *Index);
|
|
}
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::support::endian;
|
|
using namespace object;
|
|
using namespace bolt;
|
|
|
|
namespace opts {
|
|
|
|
extern cl::OptionCategory BoltCategory;
|
|
extern cl::opt<unsigned> Verbosity;
|
|
extern cl::opt<std::string> OutputFilename;
|
|
|
|
static cl::opt<bool>
|
|
KeepARanges("keep-aranges",
|
|
cl::desc("keep or generate .debug_aranges section if .gdb_index is written"),
|
|
cl::ZeroOrMore,
|
|
cl::Hidden,
|
|
cl::cat(BoltCategory));
|
|
|
|
static cl::opt<bool>
|
|
DeterministicDebugInfo("deterministic-debuginfo",
|
|
cl::desc("disables parallel execution of tasks that may produce"
|
|
"nondeterministic debug info"),
|
|
cl::init(true),
|
|
cl::cat(BoltCategory));
|
|
|
|
static cl::opt<std::string> DwarfOutputPath(
|
|
"dwarf-output-path",
|
|
cl::desc("Path to where .dwo files or dwp file will be written out to."),
|
|
cl::init(""), cl::cat(BoltCategory));
|
|
|
|
static cl::opt<bool>
|
|
WriteDWP("write-dwp",
|
|
cl::desc("output a single dwarf package file (dwp) instead of "
|
|
"multiple non-relocatable dwarf object files (dwo)."),
|
|
cl::init(false), cl::cat(BoltCategory));
|
|
|
|
static cl::opt<bool>
|
|
DebugSkeletonCu("debug-skeleton-cu",
|
|
cl::desc("prints out offsetrs for abbrev and debu_info of "
|
|
"Skeleton CUs that get patched."),
|
|
cl::ZeroOrMore, cl::Hidden, cl::init(false),
|
|
cl::cat(BoltCategory));
|
|
} // namespace opts
|
|
|
|
/// Returns DWO Name to be used. Handles case where user specifies output DWO
|
|
/// directory, and there are duplicate names. Assumes DWO ID is unique.
|
|
static std::string
|
|
getDWOName(llvm::DWARFUnit &CU,
|
|
std::unordered_map<std::string, uint32_t> *NameToIndexMap,
|
|
std::unordered_map<uint64_t, std::string> &DWOIdToName) {
|
|
llvm::Optional<uint64_t> DWOId = CU.getDWOId();
|
|
assert(DWOId && "DWO ID not found.");
|
|
(void)DWOId;
|
|
auto NameIter = DWOIdToName.find(*DWOId);
|
|
if (NameIter != DWOIdToName.end())
|
|
return NameIter->second;
|
|
|
|
std::string DWOName = dwarf::toString(
|
|
CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
|
|
"");
|
|
assert(!DWOName.empty() &&
|
|
"DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists.");
|
|
if (NameToIndexMap && !opts::DwarfOutputPath.empty()) {
|
|
auto Iter = NameToIndexMap->find(DWOName);
|
|
if (Iter == NameToIndexMap->end())
|
|
Iter = NameToIndexMap->insert({DWOName, 0}).first;
|
|
DWOName.append(std::to_string(Iter->second));
|
|
++Iter->second;
|
|
}
|
|
DWOName.append(".dwo");
|
|
DWOIdToName[*DWOId] = DWOName;
|
|
return DWOName;
|
|
}
|
|
|
|
static bool isHighPcFormEightBytes(dwarf::Form DwarfForm) {
|
|
return DwarfForm == dwarf::DW_FORM_addr || DwarfForm == dwarf::DW_FORM_data8;
|
|
}
|
|
|
|
void DWARFRewriter::updateDebugInfo() {
|
|
ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
|
|
if (!DebugInfo)
|
|
return;
|
|
|
|
auto *DebugInfoPatcher =
|
|
static_cast<DebugInfoBinaryPatcher *>(DebugInfo->getPatcher());
|
|
|
|
ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
|
|
RangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
|
|
StrWriter = std::make_unique<DebugStrWriter>(&BC);
|
|
AbbrevWriter = std::make_unique<DebugAbbrevWriter>(*BC.DwCtx);
|
|
|
|
AddrWriter = std::make_unique<DebugAddrWriter>(&BC);
|
|
DebugLoclistWriter::setAddressWriter(AddrWriter.get());
|
|
|
|
uint64_t NumCUs = BC.DwCtx->getNumCompileUnits();
|
|
if ((opts::NoThreads || opts::DeterministicDebugInfo) &&
|
|
BC.getNumDWOCUs() == 0) {
|
|
// Use single entry for efficiency when running single-threaded
|
|
NumCUs = 1;
|
|
}
|
|
|
|
LocListWritersByCU.reserve(NumCUs);
|
|
|
|
for (size_t CUIndex = 0; CUIndex < NumCUs; ++CUIndex)
|
|
LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>(&BC);
|
|
|
|
// Unordered maps to handle name collision if output DWO directory is
|
|
// specified.
|
|
std::unordered_map<std::string, uint32_t> NameToIndexMap;
|
|
std::unordered_map<uint64_t, std::string> DWOIdToName;
|
|
std::mutex AccessMutex;
|
|
|
|
auto updateDWONameCompDir = [&](DWARFUnit &Unit) -> void {
|
|
const DWARFDie &DIE = Unit.getUnitDIE();
|
|
Optional<AttrInfo> AttrInfoVal =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_GNU_dwo_name);
|
|
(void)AttrInfoVal;
|
|
assert(AttrInfoVal && "Skeleton CU doesn't have dwo_name.");
|
|
|
|
std::string ObjectName = "";
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
ObjectName = getDWOName(Unit, &NameToIndexMap, DWOIdToName);
|
|
}
|
|
|
|
uint32_t NewOffset = StrWriter->addString(ObjectName.c_str());
|
|
DebugInfoPatcher->addLE32Patch(AttrInfoVal->Offset, NewOffset,
|
|
AttrInfoVal->Size);
|
|
|
|
AttrInfoVal = findAttributeInfo(DIE, dwarf::DW_AT_comp_dir);
|
|
(void)AttrInfoVal;
|
|
assert(AttrInfoVal && "DW_AT_comp_dir is not in Skeleton CU.");
|
|
|
|
if (!opts::DwarfOutputPath.empty()) {
|
|
uint32_t NewOffset = StrWriter->addString(opts::DwarfOutputPath.c_str());
|
|
DebugInfoPatcher->addLE32Patch(AttrInfoVal->Offset, NewOffset,
|
|
AttrInfoVal->Size);
|
|
}
|
|
};
|
|
|
|
auto processUnitDIE = [&](size_t CUIndex, DWARFUnit *Unit) {
|
|
// Check if the unit is a skeleton and we need special updates for it and
|
|
// its matching split/DWO CU.
|
|
Optional<DWARFUnit *> SplitCU;
|
|
Optional<uint64_t> RangesBase;
|
|
llvm::Optional<uint64_t> DWOId = Unit->getDWOId();
|
|
if (DWOId)
|
|
SplitCU = BC.getDWOCU(*DWOId);
|
|
|
|
DebugLocWriter *DebugLocWriter = nullptr;
|
|
// Skipping CUs that failed to load.
|
|
if (SplitCU) {
|
|
updateDWONameCompDir(*Unit);
|
|
|
|
// Assuming there is unique DWOID per binary. i.e. two or more CUs don't
|
|
// have same DWO ID.
|
|
assert(LocListWritersByCU.count(*DWOId) == 0 &&
|
|
"LocList writer for DWO unit already exists.");
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
DebugLocWriter =
|
|
LocListWritersByCU
|
|
.insert(
|
|
{*DWOId, std::make_unique<DebugLoclistWriter>(&BC, *DWOId)})
|
|
.first->second.get();
|
|
}
|
|
DebugInfoBinaryPatcher *DwoDebugInfoPatcher =
|
|
llvm::cast<DebugInfoBinaryPatcher>(
|
|
getBinaryDWODebugInfoPatcher(*DWOId));
|
|
RangesBase = RangesSectionWriter->getSectionOffset();
|
|
DWARFContext *DWOCtx = BC.getDWOContext();
|
|
// Setting this CU offset with DWP to normalize DIE offsets to uint32_t
|
|
if (DWOCtx && !DWOCtx->getCUIndex().getRows().empty())
|
|
DwoDebugInfoPatcher->setDWPOffset((*SplitCU)->getOffset());
|
|
DwoDebugInfoPatcher->setRangeBase(*RangesBase);
|
|
DwoDebugInfoPatcher->addUnitBaseOffsetLabel((*SplitCU)->getOffset());
|
|
DebugAbbrevWriter *DWOAbbrevWriter =
|
|
createBinaryDWOAbbrevWriter((*SplitCU)->getContext(), *DWOId);
|
|
updateUnitDebugInfo(*(*SplitCU), *DwoDebugInfoPatcher, *DWOAbbrevWriter,
|
|
*DebugLocWriter);
|
|
DwoDebugInfoPatcher->clearDestinationLabels();
|
|
if (!DwoDebugInfoPatcher->getWasRangBasedUsed())
|
|
RangesBase = None;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
DebugLocWriter = LocListWritersByCU[CUIndex].get();
|
|
}
|
|
DebugInfoPatcher->addUnitBaseOffsetLabel(Unit->getOffset());
|
|
updateUnitDebugInfo(*Unit, *DebugInfoPatcher, *AbbrevWriter,
|
|
*DebugLocWriter, RangesBase);
|
|
};
|
|
|
|
if (opts::NoThreads || opts::DeterministicDebugInfo) {
|
|
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units())
|
|
processUnitDIE(0, CU.get());
|
|
} else {
|
|
// Update unit debug info in parallel
|
|
ThreadPool &ThreadPool = ParallelUtilities::getThreadPool();
|
|
size_t CUIndex = 0;
|
|
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
ThreadPool.async(processUnitDIE, CUIndex, CU.get());
|
|
CUIndex++;
|
|
}
|
|
ThreadPool.wait();
|
|
}
|
|
|
|
DebugInfoPatcher->clearDestinationLabels();
|
|
flushPendingRanges(*DebugInfoPatcher);
|
|
|
|
CUOffsetMap OffsetMap = finalizeDebugSections(*DebugInfoPatcher);
|
|
|
|
if (opts::WriteDWP)
|
|
writeDWP(DWOIdToName);
|
|
else
|
|
writeDWOFiles(DWOIdToName);
|
|
|
|
updateGdbIndexSection(OffsetMap);
|
|
}
|
|
|
|
void DWARFRewriter::updateUnitDebugInfo(
|
|
DWARFUnit &Unit, DebugInfoBinaryPatcher &DebugInfoPatcher,
|
|
DebugAbbrevWriter &AbbrevWriter, DebugLocWriter &DebugLocWriter,
|
|
Optional<uint64_t> RangesBase) {
|
|
// Cache debug ranges so that the offset for identical ranges could be reused.
|
|
std::map<DebugAddressRangesVector, uint64_t> CachedRanges;
|
|
|
|
uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize();
|
|
uint64_t NextCUOffset = Unit.getNextUnitOffset();
|
|
DWARFDebugInfoEntry Die;
|
|
DWARFDataExtractor DebugInfoData = Unit.getDebugInfoExtractor();
|
|
uint32_t Depth = 0;
|
|
|
|
while (
|
|
DIEOffset < NextCUOffset &&
|
|
Die.extractFast(Unit, &DIEOffset, DebugInfoData, NextCUOffset, Depth)) {
|
|
if (const DWARFAbbreviationDeclaration *AbbrDecl =
|
|
Die.getAbbreviationDeclarationPtr()) {
|
|
if (AbbrDecl->hasChildren())
|
|
++Depth;
|
|
} else {
|
|
// NULL entry.
|
|
if (Depth > 0)
|
|
--Depth;
|
|
if (Depth == 0)
|
|
break;
|
|
}
|
|
|
|
DWARFDie DIE(&Unit, &Die);
|
|
|
|
switch (DIE.getTag()) {
|
|
case dwarf::DW_TAG_compile_unit: {
|
|
auto ModuleRangesOrError = DIE.getAddressRanges();
|
|
if (!ModuleRangesOrError) {
|
|
consumeError(ModuleRangesOrError.takeError());
|
|
break;
|
|
}
|
|
DWARFAddressRangesVector &ModuleRanges = *ModuleRangesOrError;
|
|
DebugAddressRangesVector OutputRanges =
|
|
BC.translateModuleAddressRanges(ModuleRanges);
|
|
const uint64_t RangesSectionOffset =
|
|
RangesSectionWriter->addRanges(OutputRanges);
|
|
if (!Unit.isDWOUnit())
|
|
ARangesSectionWriter->addCURanges(Unit.getOffset(),
|
|
std::move(OutputRanges));
|
|
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher,
|
|
AbbrevWriter, RangesBase);
|
|
break;
|
|
}
|
|
case dwarf::DW_TAG_subprogram: {
|
|
// Get function address either from ranges or [LowPC, HighPC) pair.
|
|
bool UsesRanges = false;
|
|
uint64_t Address;
|
|
uint64_t SectionIndex, HighPC;
|
|
if (!DIE.getLowAndHighPC(Address, HighPC, SectionIndex)) {
|
|
Expected<DWARFAddressRangesVector> RangesOrError =
|
|
DIE.getAddressRanges();
|
|
if (!RangesOrError) {
|
|
consumeError(RangesOrError.takeError());
|
|
break;
|
|
}
|
|
DWARFAddressRangesVector Ranges = *RangesOrError;
|
|
// Not a function definition.
|
|
if (Ranges.empty())
|
|
break;
|
|
|
|
Address = Ranges.front().LowPC;
|
|
UsesRanges = true;
|
|
}
|
|
|
|
// Clear cached ranges as the new function will have its own set.
|
|
CachedRanges.clear();
|
|
|
|
DebugAddressRangesVector FunctionRanges;
|
|
if (const BinaryFunction *Function =
|
|
BC.getBinaryFunctionAtAddress(Address))
|
|
FunctionRanges = Function->getOutputAddressRanges();
|
|
// Update ranges.
|
|
if (UsesRanges) {
|
|
updateDWARFObjectAddressRanges(
|
|
DIE, RangesSectionWriter->addRanges(FunctionRanges),
|
|
DebugInfoPatcher, AbbrevWriter);
|
|
} else {
|
|
// Delay conversion of [LowPC, HighPC) into DW_AT_ranges if possible.
|
|
const DWARFAbbreviationDeclaration *Abbrev =
|
|
DIE.getAbbreviationDeclarationPtr();
|
|
assert(Abbrev && "abbrev expected");
|
|
|
|
// Create a critical section.
|
|
static std::shared_timed_mutex CriticalSectionMutex;
|
|
std::unique_lock<std::shared_timed_mutex> Lock(CriticalSectionMutex);
|
|
|
|
if (FunctionRanges.size() > 1) {
|
|
convertPending(Unit, Abbrev, DebugInfoPatcher, AbbrevWriter);
|
|
// Exit critical section early.
|
|
Lock.unlock();
|
|
convertToRanges(DIE, FunctionRanges, DebugInfoPatcher);
|
|
} else if (ConvertedRangesAbbrevs.find(Abbrev) !=
|
|
ConvertedRangesAbbrevs.end()) {
|
|
// Exit critical section early.
|
|
Lock.unlock();
|
|
convertToRanges(DIE, FunctionRanges, DebugInfoPatcher);
|
|
} else {
|
|
if (FunctionRanges.empty())
|
|
FunctionRanges.emplace_back(DebugAddressRange());
|
|
addToPendingRanges(Abbrev, DIE, FunctionRanges, Unit.getDWOId());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case dwarf::DW_TAG_lexical_block:
|
|
case dwarf::DW_TAG_inlined_subroutine:
|
|
case dwarf::DW_TAG_try_block:
|
|
case dwarf::DW_TAG_catch_block: {
|
|
uint64_t RangesSectionOffset =
|
|
RangesSectionWriter->getEmptyRangesOffset();
|
|
Expected<DWARFAddressRangesVector> RangesOrError = DIE.getAddressRanges();
|
|
const BinaryFunction *Function =
|
|
RangesOrError && !RangesOrError->empty()
|
|
? BC.getBinaryFunctionContainingAddress(
|
|
RangesOrError->front().LowPC)
|
|
: nullptr;
|
|
if (Function) {
|
|
DebugAddressRangesVector OutputRanges =
|
|
Function->translateInputToOutputRanges(*RangesOrError);
|
|
LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) {
|
|
dbgs() << "BOLT-DEBUG: problem with DIE at 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
|
|
<< Twine::utohexstr(Unit.getOffset()) << '\n';
|
|
});
|
|
RangesSectionOffset = RangesSectionWriter->addRanges(
|
|
std::move(OutputRanges), CachedRanges);
|
|
} else if (!RangesOrError) {
|
|
consumeError(RangesOrError.takeError());
|
|
}
|
|
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher,
|
|
AbbrevWriter);
|
|
break;
|
|
}
|
|
default: {
|
|
// Handle any tag that can have DW_AT_location attribute.
|
|
DWARFFormValue Value;
|
|
uint64_t AttrOffset;
|
|
if (Optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_location)) {
|
|
AttrOffset = AttrVal->Offset;
|
|
Value = AttrVal->V;
|
|
if (Value.isFormClass(DWARFFormValue::FC_Constant) ||
|
|
Value.isFormClass(DWARFFormValue::FC_SectionOffset)) {
|
|
uint64_t Offset = Value.isFormClass(DWARFFormValue::FC_Constant)
|
|
? Value.getAsUnsignedConstant().getValue()
|
|
: Value.getAsSectionOffset().getValue();
|
|
DebugLocationsVector InputLL;
|
|
|
|
Optional<object::SectionedAddress> SectionAddress =
|
|
Unit.getBaseAddress();
|
|
uint64_t BaseAddress = 0;
|
|
if (SectionAddress)
|
|
BaseAddress = SectionAddress->Address;
|
|
|
|
Error E = Unit.getLocationTable().visitLocationList(
|
|
&Offset, [&](const DWARFLocationEntry &Entry) {
|
|
switch (Entry.Kind) {
|
|
default:
|
|
llvm_unreachable("Unsupported DWARFLocationEntry Kind.");
|
|
case dwarf::DW_LLE_end_of_list:
|
|
return false;
|
|
case dwarf::DW_LLE_base_address:
|
|
assert(Entry.SectionIndex == SectionedAddress::UndefSection &&
|
|
"absolute address expected");
|
|
BaseAddress = Entry.Value0;
|
|
break;
|
|
case dwarf::DW_LLE_offset_pair:
|
|
assert(
|
|
(Entry.SectionIndex == SectionedAddress::UndefSection &&
|
|
!Unit.isDWOUnit()) &&
|
|
"absolute address expected");
|
|
InputLL.emplace_back(DebugLocationEntry{
|
|
BaseAddress + Entry.Value0, BaseAddress + Entry.Value1,
|
|
Entry.Loc});
|
|
break;
|
|
case dwarf::DW_LLE_startx_length:
|
|
assert(Unit.isDWOUnit() &&
|
|
"None DWO Unit with DW_LLE_startx_length encoding.");
|
|
Optional<object::SectionedAddress> EntryAddress =
|
|
Unit.getAddrOffsetSectionItem(Entry.Value0);
|
|
assert(EntryAddress && "Address does not exist.");
|
|
InputLL.emplace_back(DebugLocationEntry{
|
|
EntryAddress->Address,
|
|
EntryAddress->Address + Entry.Value1, Entry.Loc});
|
|
break;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (E || InputLL.empty()) {
|
|
errs() << "BOLT-WARNING: empty location list detected at 0x"
|
|
<< Twine::utohexstr(Offset) << " for DIE at 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
|
|
<< Twine::utohexstr(Unit.getOffset()) << '\n';
|
|
} else {
|
|
const uint64_t Address = InputLL.front().LowPC;
|
|
if (const BinaryFunction *Function =
|
|
BC.getBinaryFunctionContainingAddress(Address)) {
|
|
DebugLocationsVector OutputLL =
|
|
Function->translateInputToOutputLocationList(InputLL);
|
|
LLVM_DEBUG(if (OutputLL.empty()) {
|
|
dbgs() << "BOLT-DEBUG: location list translated to an empty "
|
|
"one at 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
|
|
<< Twine::utohexstr(Unit.getOffset()) << '\n';
|
|
});
|
|
DebugLocWriter.addList(AttrOffset, std::move(OutputLL));
|
|
}
|
|
}
|
|
} else {
|
|
assert((Value.isFormClass(DWARFFormValue::FC_Exprloc) ||
|
|
Value.isFormClass(DWARFFormValue::FC_Block)) &&
|
|
"unexpected DW_AT_location form");
|
|
if (Unit.isDWOUnit()) {
|
|
ArrayRef<uint8_t> Expr = *Value.getAsBlock();
|
|
DataExtractor Data(
|
|
StringRef((const char *)Expr.data(), Expr.size()),
|
|
Unit.getContext().isLittleEndian(), 0);
|
|
DWARFExpression LocExpr(Data, Unit.getAddressByteSize(),
|
|
Unit.getFormParams().Format);
|
|
for (auto &Expr : LocExpr) {
|
|
if (Expr.getCode() != dwarf::DW_OP_GNU_addr_index)
|
|
continue;
|
|
uint64_t Index = Expr.getRawOperand(0);
|
|
Optional<object::SectionedAddress> EntryAddress =
|
|
Unit.getAddrOffsetSectionItem(Index);
|
|
assert(EntryAddress && "Address is not found.");
|
|
assert(Index <= std::numeric_limits<uint32_t>::max() &&
|
|
"Invalid Operand Index.");
|
|
AddrWriter->addIndexAddress(EntryAddress->Address,
|
|
static_cast<uint32_t>(Index),
|
|
*Unit.getDWOId());
|
|
}
|
|
}
|
|
}
|
|
} else if (Optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_low_pc)) {
|
|
AttrOffset = AttrVal->Offset;
|
|
Value = AttrVal->V;
|
|
const Optional<uint64_t> Result = Value.getAsAddress();
|
|
if (Result.hasValue()) {
|
|
const uint64_t Address = Result.getValue();
|
|
uint64_t NewAddress = 0;
|
|
if (const BinaryFunction *Function =
|
|
BC.getBinaryFunctionContainingAddress(Address)) {
|
|
NewAddress = Function->translateInputToOutputAddress(Address);
|
|
LLVM_DEBUG(dbgs()
|
|
<< "BOLT-DEBUG: Fixing low_pc 0x"
|
|
<< Twine::utohexstr(Address) << " for DIE with tag "
|
|
<< DIE.getTag() << " to 0x"
|
|
<< Twine::utohexstr(NewAddress) << '\n');
|
|
}
|
|
|
|
dwarf::Form Form = Value.getForm();
|
|
assert(Form != dwarf::DW_FORM_LLVM_addrx_offset &&
|
|
"DW_FORM_LLVM_addrx_offset is not supported");
|
|
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
|
if (Form == dwarf::DW_FORM_GNU_addr_index) {
|
|
assert(Unit.isDWOUnit() &&
|
|
"DW_FORM_GNU_addr_index in Non DWO unit.");
|
|
uint64_t Index = Value.getRawUValue();
|
|
// If there is no new address, storing old address.
|
|
// Re-using Index to make implementation easier.
|
|
// DW_FORM_GNU_addr_index is variable lenght encoding so we either
|
|
// have to create indices of same sizes, or use same index.
|
|
AddrWriter->addIndexAddress(NewAddress ? NewAddress : Address,
|
|
Index, *Unit.getDWOId());
|
|
} else {
|
|
DebugInfoPatcher.addLE64Patch(AttrOffset, NewAddress);
|
|
}
|
|
} else if (opts::Verbosity >= 1) {
|
|
errs() << "BOLT-WARNING: unexpected form value for attribute at 0x"
|
|
<< Twine::utohexstr(AttrOffset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handling references.
|
|
assert(DIE.isValid() && "Invalid DIE.");
|
|
const DWARFAbbreviationDeclaration *AbbrevDecl =
|
|
DIE.getAbbreviationDeclarationPtr();
|
|
if (!AbbrevDecl)
|
|
continue;
|
|
uint32_t Index = 0;
|
|
for (const DWARFAbbreviationDeclaration::AttributeSpec &Decl :
|
|
AbbrevDecl->attributes()) {
|
|
switch (Decl.Form) {
|
|
default:
|
|
break;
|
|
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_addr: {
|
|
Optional<AttrInfo> AttrVal = findAttributeInfo(DIE, AbbrevDecl, Index);
|
|
uint32_t DestinationAddress =
|
|
AttrVal->V.getRawUValue() +
|
|
(Decl.Form == dwarf::DW_FORM_ref_addr ? 0 : Unit.getOffset());
|
|
DebugInfoPatcher.addReferenceToPatch(
|
|
AttrVal->Offset, DestinationAddress, AttrVal->Size, Decl.Form);
|
|
// We can have only one reference, and it can be backward one.
|
|
DebugInfoPatcher.addDestinationReferenceLabel(DestinationAddress);
|
|
break;
|
|
}
|
|
}
|
|
++Index;
|
|
}
|
|
}
|
|
if (DIEOffset > NextCUOffset)
|
|
errs() << "BOLT-WARNING: corrupt DWARF detected at 0x"
|
|
<< Twine::utohexstr(Unit.getOffset()) << '\n';
|
|
}
|
|
|
|
void DWARFRewriter::updateDWARFObjectAddressRanges(
|
|
const DWARFDie DIE, uint64_t DebugRangesOffset,
|
|
SimpleBinaryPatcher &DebugInfoPatcher, DebugAbbrevWriter &AbbrevWriter,
|
|
Optional<uint64_t> RangesBase) {
|
|
|
|
// Some objects don't have an associated DIE and cannot be updated (such as
|
|
// compiler-generated functions).
|
|
if (!DIE)
|
|
return;
|
|
|
|
const DWARFAbbreviationDeclaration *AbbreviationDecl =
|
|
DIE.getAbbreviationDeclarationPtr();
|
|
if (!AbbreviationDecl) {
|
|
if (opts::Verbosity >= 1)
|
|
errs() << "BOLT-WARNING: object's DIE doesn't have an abbreviation: "
|
|
<< "skipping update. DIE at offset 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
|
return;
|
|
}
|
|
|
|
if (RangesBase) {
|
|
// If DW_AT_GNU_ranges_base is present, update it. No further modifications
|
|
// are needed for ranges base.
|
|
Optional<AttrInfo> RangesBaseAttrInfo =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_GNU_ranges_base);
|
|
if (RangesBaseAttrInfo) {
|
|
DebugInfoPatcher.addLE32Patch(RangesBaseAttrInfo->Offset,
|
|
static_cast<uint32_t>(*RangesBase),
|
|
RangesBaseAttrInfo->Size);
|
|
RangesBase = None;
|
|
}
|
|
}
|
|
|
|
Optional<AttrInfo> LowPCAttrInfo =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_low_pc);
|
|
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_ranges)) {
|
|
// Case 1: The object was already non-contiguous and had DW_AT_ranges.
|
|
// In this case we simply need to update the value of DW_AT_ranges
|
|
// and introduce DW_AT_GNU_ranges_base if required.
|
|
Optional<AttrInfo> AttrVal = findAttributeInfo(DIE, dwarf::DW_AT_ranges);
|
|
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
|
DebugInfoPatcher.addLE32Patch(
|
|
AttrVal->Offset, DebugRangesOffset - DebugInfoPatcher.getRangeBase(),
|
|
AttrVal->Size);
|
|
|
|
if (!RangesBase) {
|
|
if (LowPCAttrInfo &&
|
|
LowPCAttrInfo->V.getForm() != dwarf::DW_FORM_GNU_addr_index &&
|
|
LowPCAttrInfo->V.getForm() != dwarf::DW_FORM_addrx)
|
|
DebugInfoPatcher.addLE64Patch(LowPCAttrInfo->Offset, 0);
|
|
return;
|
|
}
|
|
|
|
// Convert DW_AT_low_pc into DW_AT_GNU_ranges_base.
|
|
if (!LowPCAttrInfo) {
|
|
errs() << "BOLT-ERROR: skeleton CU at 0x"
|
|
<< Twine::utohexstr(DIE.getOffset())
|
|
<< " does not have DW_AT_GNU_ranges_base or DW_AT_low_pc to"
|
|
" convert to update ranges base\n";
|
|
return;
|
|
}
|
|
|
|
AbbrevWriter.addAttributePatch(
|
|
*DIE.getDwarfUnit(), AbbreviationDecl, dwarf::DW_AT_low_pc,
|
|
dwarf::DW_AT_GNU_ranges_base, dwarf::DW_FORM_indirect);
|
|
DebugInfoPatcher.addUDataPatch(LowPCAttrInfo->Offset, dwarf::DW_FORM_udata,
|
|
1);
|
|
DebugInfoPatcher.addUDataPatch(LowPCAttrInfo->Offset + 1, *RangesBase, 7);
|
|
|
|
return;
|
|
}
|
|
|
|
// Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back
|
|
// to back. Replace with new attributes and patch the DIE.
|
|
Optional<AttrInfo> HighPCAttrInfo =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_high_pc);
|
|
if (LowPCAttrInfo && HighPCAttrInfo) {
|
|
convertToRangesPatchAbbrev(*DIE.getDwarfUnit(), AbbreviationDecl,
|
|
AbbrevWriter, RangesBase);
|
|
convertToRangesPatchDebugInfo(DIE, DebugRangesOffset, DebugInfoPatcher,
|
|
RangesBase);
|
|
} else {
|
|
if (opts::Verbosity >= 1)
|
|
errs() << "BOLT-ERROR: cannot update ranges for DIE at offset 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
|
}
|
|
}
|
|
|
|
void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) {
|
|
ErrorOr<BinarySection &> DbgInfoSection =
|
|
BC.getUniqueSectionByName(".debug_info");
|
|
ErrorOr<BinarySection &> TypeInfoSection =
|
|
BC.getUniqueSectionByName(".debug_types");
|
|
assert(((BC.DwCtx->getNumTypeUnits() > 0 && TypeInfoSection) ||
|
|
BC.DwCtx->getNumTypeUnits() == 0) &&
|
|
"Was not able to retrieve Debug Types section.");
|
|
|
|
// We will be re-writing .debug_info so relocation mechanism doesn't work for
|
|
// Debug Info Patcher.
|
|
DebugInfoBinaryPatcher *DebugInfoPatcher = nullptr;
|
|
if (BC.DwCtx->getNumCompileUnits()) {
|
|
DbgInfoSection->registerPatcher(std::make_unique<DebugInfoBinaryPatcher>());
|
|
DebugInfoPatcher =
|
|
static_cast<DebugInfoBinaryPatcher *>(DbgInfoSection->getPatcher());
|
|
}
|
|
|
|
// There is no direct connection between CU and TU, but same offsets,
|
|
// encoded in DW_AT_stmt_list, into .debug_line get modified.
|
|
// We take advantage of that to map original CU line table offsets to new
|
|
// ones.
|
|
std::unordered_map<uint64_t, uint64_t> DebugLineOffsetMap;
|
|
|
|
auto GetStatementListValue = [](DWARFUnit *Unit) {
|
|
Optional<DWARFFormValue> StmtList =
|
|
Unit->getUnitDIE().find(dwarf::DW_AT_stmt_list);
|
|
Optional<uint64_t> Offset = dwarf::toSectionOffset(StmtList);
|
|
assert(Offset && "Was not able to retreive value of DW_AT_stmt_list.");
|
|
return *Offset;
|
|
};
|
|
|
|
const uint64_t Reloc32Type = BC.isAArch64()
|
|
? static_cast<uint64_t>(ELF::R_AARCH64_ABS32)
|
|
: static_cast<uint64_t>(ELF::R_X86_64_32);
|
|
|
|
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
const unsigned CUID = CU->getOffset();
|
|
MCSymbol *Label = BC.getDwarfLineTable(CUID).getLabel();
|
|
if (!Label)
|
|
continue;
|
|
|
|
Optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(CU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list);
|
|
if (!AttrVal)
|
|
continue;
|
|
|
|
const uint64_t AttributeOffset = AttrVal->Offset;
|
|
const uint64_t LineTableOffset = Layout.getSymbolOffset(*Label);
|
|
DebugLineOffsetMap[GetStatementListValue(CU.get())] = LineTableOffset;
|
|
assert(DbgInfoSection && ".debug_info section must exist");
|
|
DebugInfoPatcher->addLE32Patch(AttributeOffset, LineTableOffset);
|
|
}
|
|
|
|
for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units()) {
|
|
DWARFUnit *Unit = TU.get();
|
|
Optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(TU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list);
|
|
if (!AttrVal)
|
|
continue;
|
|
const uint64_t AttributeOffset = AttrVal->Offset;
|
|
auto Iter = DebugLineOffsetMap.find(GetStatementListValue(Unit));
|
|
assert(Iter != DebugLineOffsetMap.end() &&
|
|
"Type Unit Updated Line Number Entry does not exist.");
|
|
TypeInfoSection->addRelocation(AttributeOffset, nullptr, Reloc32Type,
|
|
Iter->second, 0, /*Pending=*/true);
|
|
}
|
|
|
|
// Set .debug_info as finalized so it won't be skipped over when
|
|
// we process sections while writing out the new binary. This ensures
|
|
// that the pending relocations will be processed and not ignored.
|
|
if (DbgInfoSection)
|
|
DbgInfoSection->setIsFinalized();
|
|
|
|
if (TypeInfoSection)
|
|
TypeInfoSection->setIsFinalized();
|
|
}
|
|
|
|
CUOffsetMap
|
|
DWARFRewriter::finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher) {
|
|
if (StrWriter->isInitialized()) {
|
|
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
|
|
std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
|
|
StrWriter->finalize();
|
|
BC.registerOrUpdateNoteSection(".debug_str",
|
|
copyByteArray(*DebugStrSectionContents),
|
|
DebugStrSectionContents->size());
|
|
}
|
|
|
|
std::unique_ptr<DebugBufferVector> RangesSectionContents =
|
|
RangesSectionWriter->finalize();
|
|
BC.registerOrUpdateNoteSection(".debug_ranges",
|
|
copyByteArray(*RangesSectionContents),
|
|
RangesSectionContents->size());
|
|
|
|
std::unique_ptr<DebugBufferVector> LocationListSectionContents =
|
|
makeFinalLocListsSection(DebugInfoPatcher);
|
|
BC.registerOrUpdateNoteSection(".debug_loc",
|
|
copyByteArray(*LocationListSectionContents),
|
|
LocationListSectionContents->size());
|
|
|
|
// AddrWriter should be finalized after debug_loc since more addresses can be
|
|
// added there.
|
|
if (AddrWriter->isInitialized()) {
|
|
AddressSectionBuffer AddressSectionContents = AddrWriter->finalize();
|
|
BC.registerOrUpdateNoteSection(".debug_addr",
|
|
copyByteArray(AddressSectionContents),
|
|
AddressSectionContents.size());
|
|
for (auto &CU : BC.DwCtx->compile_units()) {
|
|
DWARFDie DIE = CU->getUnitDIE();
|
|
if (Optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(DIE, dwarf::DW_AT_GNU_addr_base)) {
|
|
uint64_t Offset = AddrWriter->getOffset(*CU->getDWOId());
|
|
DebugInfoPatcher.addLE32Patch(
|
|
AttrVal->Offset, static_cast<int32_t>(Offset), AttrVal->Size);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<DebugBufferVector> AbbrevSectionContents =
|
|
AbbrevWriter->finalize();
|
|
BC.registerOrUpdateNoteSection(".debug_abbrev",
|
|
copyByteArray(*AbbrevSectionContents),
|
|
AbbrevSectionContents->size());
|
|
|
|
// Update abbreviation offsets for CUs/TUs if they were changed.
|
|
SimpleBinaryPatcher *DebugTypesPatcher = nullptr;
|
|
for (auto &Unit : BC.DwCtx->normal_units()) {
|
|
const uint64_t NewAbbrevOffset =
|
|
AbbrevWriter->getAbbreviationsOffsetForUnit(*Unit);
|
|
if (Unit->getAbbreviationsOffset() == NewAbbrevOffset)
|
|
continue;
|
|
|
|
// DWARFv4
|
|
// unit_length - 4 bytes
|
|
// version - 2 bytes
|
|
// So + 6 to patch debug_abbrev_offset
|
|
constexpr uint64_t AbbrevFieldOffset = 6;
|
|
if (!Unit->isTypeUnit()) {
|
|
DebugInfoPatcher.addLE32Patch(Unit->getOffset() + AbbrevFieldOffset,
|
|
static_cast<uint32_t>(NewAbbrevOffset));
|
|
continue;
|
|
}
|
|
|
|
if (!DebugTypesPatcher) {
|
|
ErrorOr<BinarySection &> DebugTypes =
|
|
BC.getUniqueSectionByName(".debug_types");
|
|
DebugTypes->registerPatcher(std::make_unique<SimpleBinaryPatcher>());
|
|
DebugTypesPatcher =
|
|
static_cast<SimpleBinaryPatcher *>(DebugTypes->getPatcher());
|
|
}
|
|
DebugTypesPatcher->addLE32Patch(Unit->getOffset() + AbbrevFieldOffset,
|
|
static_cast<uint32_t>(NewAbbrevOffset));
|
|
}
|
|
|
|
// No more creating new DebugInfoPatches.
|
|
CUOffsetMap CUMap =
|
|
DebugInfoPatcher.computeNewOffsets(*BC.DwCtx.get(), false);
|
|
|
|
// Skip .debug_aranges if we are re-generating .gdb_index.
|
|
if (opts::KeepARanges || !BC.getGdbIndexSection()) {
|
|
SmallVector<char, 16> ARangesBuffer;
|
|
raw_svector_ostream OS(ARangesBuffer);
|
|
|
|
auto MAB = std::unique_ptr<MCAsmBackend>(
|
|
BC.TheTarget->createMCAsmBackend(*BC.STI, *BC.MRI, MCTargetOptions()));
|
|
|
|
ARangesSectionWriter->writeARangesSection(OS, CUMap);
|
|
const StringRef &ARangesContents = OS.str();
|
|
|
|
BC.registerOrUpdateNoteSection(".debug_aranges",
|
|
copyByteArray(ARangesContents),
|
|
ARangesContents.size());
|
|
}
|
|
return CUMap;
|
|
}
|
|
|
|
// Creates all the data structures necessary for creating MCStreamer.
|
|
// They are passed by reference because they need to be kept around.
|
|
// Also creates known debug sections. These are sections handled by
|
|
// handleDebugDataPatching.
|
|
using KnownSectionsEntry = std::pair<MCSection *, DWARFSectionKind>;
|
|
namespace {
|
|
|
|
std::unique_ptr<BinaryContext>
|
|
createDwarfOnlyBC(const object::ObjectFile &File) {
|
|
return BinaryContext::createBinaryContext(
|
|
&File, false,
|
|
DWARFContext::create(File, DWARFContext::ProcessDebugRelocations::Ignore,
|
|
nullptr, "", WithColor::defaultErrorHandler,
|
|
WithColor::defaultWarningHandler));
|
|
}
|
|
|
|
StringMap<KnownSectionsEntry>
|
|
createKnownSectionsMap(const MCObjectFileInfo &MCOFI) {
|
|
StringMap<KnownSectionsEntry> KnownSectionsTemp = {
|
|
{"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}},
|
|
{"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_EXT_TYPES}},
|
|
{"debug_str_offsets.dwo",
|
|
{MCOFI.getDwarfStrOffDWOSection(), DW_SECT_STR_OFFSETS}},
|
|
{"debug_str.dwo", {MCOFI.getDwarfStrDWOSection(), DW_SECT_EXT_unknown}},
|
|
{"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_EXT_LOC}},
|
|
{"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}},
|
|
{"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}};
|
|
return KnownSectionsTemp;
|
|
}
|
|
|
|
StringRef getSectionName(const SectionRef &Section) {
|
|
Expected<StringRef> SectionName = Section.getName();
|
|
assert(SectionName && "Invalid section name.");
|
|
StringRef Name = *SectionName;
|
|
Name = Name.substr(Name.find_first_not_of("._"));
|
|
return Name;
|
|
}
|
|
|
|
// Exctracts an appropriate slice if input is DWP.
|
|
// Applies patches or overwrites the section.
|
|
Optional<StringRef> updateDebugData(
|
|
DWARFContext &DWCtx, std::string &Storage, const SectionRef &Section,
|
|
const StringMap<KnownSectionsEntry> &KnownSections, MCStreamer &Streamer,
|
|
DWARFRewriter &Writer, const DWARFUnitIndex::Entry *DWOEntry,
|
|
uint64_t DWOId, std::unique_ptr<DebugBufferVector> &OutputBuffer) {
|
|
auto applyPatch = [&](DebugInfoBinaryPatcher *Patcher,
|
|
StringRef Data) -> StringRef {
|
|
Patcher->computeNewOffsets(DWCtx, true);
|
|
Storage = Patcher->patchBinary(Data);
|
|
return StringRef(Storage.c_str(), Storage.size());
|
|
};
|
|
|
|
using DWOSectionContribution =
|
|
const DWARFUnitIndex::Entry::SectionContribution;
|
|
auto getSliceData = [&](const DWARFUnitIndex::Entry *DWOEntry,
|
|
StringRef OutData, DWARFSectionKind Sec,
|
|
uint32_t &DWPOffset) -> StringRef {
|
|
if (DWOEntry) {
|
|
DWOSectionContribution *DWOContrubution = DWOEntry->getContribution(Sec);
|
|
DWPOffset = DWOContrubution->Offset;
|
|
OutData = OutData.substr(DWPOffset, DWOContrubution->Length);
|
|
}
|
|
return OutData;
|
|
};
|
|
|
|
StringRef Name = getSectionName(Section);
|
|
auto SectionIter = KnownSections.find(Name);
|
|
if (SectionIter == KnownSections.end())
|
|
return None;
|
|
Streamer.SwitchSection(SectionIter->second.first);
|
|
Expected<StringRef> Contents = Section.getContents();
|
|
assert(Contents && "Invalid contents.");
|
|
StringRef OutData = *Contents;
|
|
uint32_t DWPOffset = 0;
|
|
|
|
switch (SectionIter->second.second) {
|
|
default: {
|
|
if (!Name.equals("debug_str.dwo"))
|
|
errs() << "BOLT-WARNING: Unsupported Debug section: " << Name << "\n";
|
|
return OutData;
|
|
}
|
|
case DWARFSectionKind::DW_SECT_INFO: {
|
|
OutData = getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_INFO,
|
|
DWPOffset);
|
|
DebugInfoBinaryPatcher *Patcher = llvm::cast<DebugInfoBinaryPatcher>(
|
|
Writer.getBinaryDWODebugInfoPatcher(DWOId));
|
|
return applyPatch(Patcher, OutData);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_EXT_TYPES: {
|
|
return getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_EXT_TYPES,
|
|
DWPOffset);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_STR_OFFSETS: {
|
|
return getSliceData(DWOEntry, OutData,
|
|
DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_ABBREV: {
|
|
DebugAbbrevWriter *AbbrevWriter = Writer.getBinaryDWOAbbrevWriter(DWOId);
|
|
OutputBuffer = AbbrevWriter->finalize();
|
|
// Creating explicit StringRef here, otherwise
|
|
// with impicit conversion it will take null byte as end of
|
|
// string.
|
|
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
|
|
OutputBuffer->size());
|
|
}
|
|
case DWARFSectionKind::DW_SECT_EXT_LOC: {
|
|
DebugLocWriter *LocWriter = Writer.getDebugLocWriter(DWOId);
|
|
OutputBuffer = LocWriter->getBuffer();
|
|
// Creating explicit StringRef here, otherwise
|
|
// with impicit conversion it will take null byte as end of
|
|
// string.
|
|
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
|
|
OutputBuffer->size());
|
|
}
|
|
case DWARFSectionKind::DW_SECT_LINE: {
|
|
return getSliceData(DWOEntry, OutData, DWARFSectionKind::DW_SECT_LINE,
|
|
DWPOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void DWARFRewriter::writeDWP(
|
|
std::unordered_map<uint64_t, std::string> &DWOIdToName) {
|
|
SmallString<0> OutputNameStr;
|
|
StringRef OutputName;
|
|
if (opts::DwarfOutputPath.empty()) {
|
|
OutputName =
|
|
Twine(opts::OutputFilename).concat(".dwp").toStringRef(OutputNameStr);
|
|
} else {
|
|
StringRef ExeFileName = llvm::sys::path::filename(opts::OutputFilename);
|
|
OutputName = Twine(opts::DwarfOutputPath)
|
|
.concat("/")
|
|
.concat(ExeFileName)
|
|
.concat(".dwp")
|
|
.toStringRef(OutputNameStr);
|
|
errs() << "BOLT-WARNING: dwarf-output-path is in effect and .dwp file will "
|
|
"possibly be written to another location that is not the same as "
|
|
"the executable\n";
|
|
}
|
|
std::error_code EC;
|
|
std::unique_ptr<ToolOutputFile> Out =
|
|
std::make_unique<ToolOutputFile>(OutputName, EC, sys::fs::OF_None);
|
|
|
|
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
|
|
std::unique_ptr<BinaryContext> TmpBC = createDwarfOnlyBC(*File);
|
|
std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(Out->os());
|
|
const MCObjectFileInfo &MCOFI = *Streamer->getContext().getObjectFileInfo();
|
|
StringMap<KnownSectionsEntry> KnownSections = createKnownSectionsMap(MCOFI);
|
|
MCSection *const StrSection = MCOFI.getDwarfStrDWOSection();
|
|
MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection();
|
|
|
|
// Data Structures for DWP book keeping
|
|
// Size of array corresponds to the number of sections supported by DWO format
|
|
// in DWARF4/5.
|
|
uint32_t ContributionOffsets[8] = {};
|
|
std::deque<SmallString<32>> UncompressedSections;
|
|
DWPStringPool Strings(*Streamer, StrSection);
|
|
MapVector<uint64_t, UnitIndexEntry> IndexEntries;
|
|
constexpr uint32_t IndexVersion = 2;
|
|
|
|
// Setup DWP code once.
|
|
DWARFContext *DWOCtx = BC.getDWOContext();
|
|
const DWARFUnitIndex *CUIndex = nullptr;
|
|
bool IsDWP = false;
|
|
if (DWOCtx) {
|
|
CUIndex = &DWOCtx->getCUIndex();
|
|
IsDWP = !CUIndex->getRows().empty();
|
|
}
|
|
|
|
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
Optional<uint64_t> DWOId = CU->getDWOId();
|
|
if (!DWOId)
|
|
continue;
|
|
|
|
// Skipping CUs that we failed to load.
|
|
Optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId);
|
|
if (!DWOCU)
|
|
continue;
|
|
|
|
assert(CU->getVersion() == 4 && "For DWP output only DWARF4 is supported");
|
|
UnitIndexEntry CurEntry = {};
|
|
CurEntry.DWOName =
|
|
dwarf::toString(CU->getUnitDIE().find(
|
|
{dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
|
|
"");
|
|
const char *Name = CU->getUnitDIE().getShortName();
|
|
if (Name)
|
|
CurEntry.Name = Name;
|
|
StringRef CurStrSection;
|
|
StringRef CurStrOffsetSection;
|
|
|
|
// This maps each section contained in this file to its length.
|
|
// This information is later on used to calculate the contributions,
|
|
// i.e. offset and length, of each compile/type unit to a section.
|
|
std::vector<std::pair<DWARFSectionKind, uint32_t>> SectionLength;
|
|
|
|
const DWARFUnitIndex::Entry *DWOEntry = nullptr;
|
|
if (IsDWP)
|
|
DWOEntry = CUIndex->getFromHash(*DWOId);
|
|
|
|
bool StrSectionWrittenOut = false;
|
|
const object::ObjectFile *DWOFile =
|
|
(*DWOCU)->getContext().getDWARFObj().getFile();
|
|
for (const SectionRef &Section : DWOFile->sections()) {
|
|
std::string Storage = "";
|
|
std::unique_ptr<DebugBufferVector> OutputData;
|
|
Optional<StringRef> TOutData = updateDebugData(
|
|
(*DWOCU)->getContext(), Storage, Section, KnownSections, *Streamer,
|
|
*this, DWOEntry, *DWOId, OutputData);
|
|
if (!TOutData)
|
|
continue;
|
|
|
|
StringRef OutData = *TOutData;
|
|
StringRef Name = getSectionName(Section);
|
|
if (Name.equals("debug_str.dwo")) {
|
|
CurStrSection = OutData;
|
|
} else {
|
|
// Since handleDebugDataPatching returned true, we already know this is
|
|
// a known section.
|
|
auto SectionIter = KnownSections.find(Name);
|
|
if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS)
|
|
CurStrOffsetSection = OutData;
|
|
else
|
|
Streamer->emitBytes(OutData);
|
|
auto Index =
|
|
getContributionIndex(SectionIter->second.second, IndexVersion);
|
|
CurEntry.Contributions[Index].Offset = ContributionOffsets[Index];
|
|
CurEntry.Contributions[Index].Length = OutData.size();
|
|
ContributionOffsets[Index] += CurEntry.Contributions[Index].Length;
|
|
}
|
|
|
|
// Strings are combined in to a new string section, and de-duplicated
|
|
// based on hash.
|
|
if (!StrSectionWrittenOut && !CurStrOffsetSection.empty() &&
|
|
!CurStrSection.empty()) {
|
|
writeStringsAndOffsets(*Streamer.get(), Strings, StrOffsetSection,
|
|
CurStrSection, CurStrOffsetSection,
|
|
CU->getVersion());
|
|
StrSectionWrittenOut = true;
|
|
}
|
|
}
|
|
CompileUnitIdentifiers CUI{*DWOId, CurEntry.Name.c_str(),
|
|
CurEntry.DWOName.c_str()};
|
|
auto P = IndexEntries.insert(std::make_pair(CUI.Signature, CurEntry));
|
|
if (!P.second) {
|
|
Error Err = buildDuplicateError(*P.first, CUI, "");
|
|
errs() << "BOLT-ERROR: " << toString(std::move(Err)) << "\n";
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Lie about the type contribution for DWARF < 5. In DWARFv5 the type
|
|
// section does not exist, so no need to do anything about this.
|
|
ContributionOffsets[getContributionIndex(DW_SECT_EXT_TYPES, 2)] = 0;
|
|
writeIndex(*Streamer.get(), MCOFI.getDwarfCUIndexSection(),
|
|
ContributionOffsets, IndexEntries, IndexVersion);
|
|
|
|
Streamer->Finish();
|
|
Out->keep();
|
|
}
|
|
|
|
void DWARFRewriter::writeDWOFiles(
|
|
std::unordered_map<uint64_t, std::string> &DWOIdToName) {
|
|
// Setup DWP code once.
|
|
DWARFContext *DWOCtx = BC.getDWOContext();
|
|
const DWARFUnitIndex *CUIndex = nullptr;
|
|
bool IsDWP = false;
|
|
if (DWOCtx) {
|
|
CUIndex = &DWOCtx->getCUIndex();
|
|
IsDWP = !CUIndex->getRows().empty();
|
|
}
|
|
|
|
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
Optional<uint64_t> DWOId = CU->getDWOId();
|
|
if (!DWOId)
|
|
continue;
|
|
|
|
// Skipping CUs that we failed to load.
|
|
Optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId);
|
|
if (!DWOCU)
|
|
continue;
|
|
|
|
std::string CompDir = opts::DwarfOutputPath.empty()
|
|
? CU->getCompilationDir()
|
|
: opts::DwarfOutputPath.c_str();
|
|
std::string ObjectName = getDWOName(*CU.get(), nullptr, DWOIdToName);
|
|
auto FullPath = CompDir.append("/").append(ObjectName);
|
|
|
|
std::error_code EC;
|
|
std::unique_ptr<ToolOutputFile> TempOut =
|
|
std::make_unique<ToolOutputFile>(FullPath, EC, sys::fs::OF_None);
|
|
|
|
const DWARFUnitIndex::Entry *DWOEntry = nullptr;
|
|
if (IsDWP)
|
|
DWOEntry = CUIndex->getFromHash(*DWOId);
|
|
|
|
const object::ObjectFile *File =
|
|
(*DWOCU)->getContext().getDWARFObj().getFile();
|
|
std::unique_ptr<BinaryContext> TmpBC = createDwarfOnlyBC(*File);
|
|
std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(TempOut->os());
|
|
StringMap<KnownSectionsEntry> KnownSections =
|
|
createKnownSectionsMap(*Streamer->getContext().getObjectFileInfo());
|
|
|
|
for (const SectionRef &Section : File->sections()) {
|
|
std::string Storage = "";
|
|
std::unique_ptr<DebugBufferVector> OutputData;
|
|
if (Optional<StringRef> OutData = updateDebugData(
|
|
(*DWOCU)->getContext(), Storage, Section, KnownSections,
|
|
*Streamer, *this, DWOEntry, *DWOId, OutputData))
|
|
Streamer->emitBytes(*OutData);
|
|
}
|
|
Streamer->Finish();
|
|
TempOut->keep();
|
|
}
|
|
}
|
|
|
|
void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap) {
|
|
if (!BC.getGdbIndexSection())
|
|
return;
|
|
|
|
// See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
|
|
// for .gdb_index section format.
|
|
|
|
StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents();
|
|
|
|
const char *Data = GdbIndexContents.data();
|
|
|
|
// Parse the header.
|
|
const uint32_t Version = read32le(Data);
|
|
if (Version != 7 && Version != 8) {
|
|
errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n";
|
|
exit(1);
|
|
}
|
|
|
|
// Some .gdb_index generators use file offsets while others use section
|
|
// offsets. Hence we can only rely on offsets relative to each other,
|
|
// and ignore their absolute values.
|
|
const uint32_t CUListOffset = read32le(Data + 4);
|
|
const uint32_t CUTypesOffset = read32le(Data + 8);
|
|
const uint32_t AddressTableOffset = read32le(Data + 12);
|
|
const uint32_t SymbolTableOffset = read32le(Data + 16);
|
|
const uint32_t ConstantPoolOffset = read32le(Data + 20);
|
|
Data += 24;
|
|
|
|
// Map CUs offsets to indices and verify existing index table.
|
|
std::map<uint32_t, uint32_t> OffsetToIndexMap;
|
|
const uint32_t CUListSize = CUTypesOffset - CUListOffset;
|
|
const unsigned NumCUs = BC.DwCtx->getNumCompileUnits();
|
|
if (CUListSize != NumCUs * 16) {
|
|
errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n";
|
|
exit(1);
|
|
}
|
|
for (unsigned Index = 0; Index < NumCUs; ++Index, Data += 16) {
|
|
const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index);
|
|
const uint64_t Offset = read64le(Data);
|
|
if (CU->getOffset() != Offset) {
|
|
errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n";
|
|
exit(1);
|
|
}
|
|
|
|
OffsetToIndexMap[Offset] = Index;
|
|
}
|
|
|
|
// Ignore old address table.
|
|
const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset;
|
|
// Move Data to the beginning of symbol table.
|
|
Data += SymbolTableOffset - CUTypesOffset;
|
|
|
|
// Calculate the size of the new address table.
|
|
uint32_t NewAddressTableSize = 0;
|
|
for (const auto &CURangesPair : ARangesSectionWriter->getCUAddressRanges()) {
|
|
const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second;
|
|
NewAddressTableSize += Ranges.size() * 20;
|
|
}
|
|
|
|
// Difference between old and new table (and section) sizes.
|
|
// Could be negative.
|
|
int32_t Delta = NewAddressTableSize - OldAddressTableSize;
|
|
|
|
size_t NewGdbIndexSize = GdbIndexContents.size() + Delta;
|
|
|
|
// Free'd by ExecutableFileMemoryManager.
|
|
auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize];
|
|
uint8_t *Buffer = NewGdbIndexContents;
|
|
|
|
write32le(Buffer, Version);
|
|
write32le(Buffer + 4, CUListOffset);
|
|
write32le(Buffer + 8, CUTypesOffset);
|
|
write32le(Buffer + 12, AddressTableOffset);
|
|
write32le(Buffer + 16, SymbolTableOffset + Delta);
|
|
write32le(Buffer + 20, ConstantPoolOffset + Delta);
|
|
Buffer += 24;
|
|
|
|
// Writing out CU List <Offset, Size>
|
|
for (auto &CUInfo : CUMap) {
|
|
write64le(Buffer, CUInfo.second.Offset);
|
|
// Length encoded in CU doesn't contain first 4 bytes that encode length.
|
|
write64le(Buffer + 8, CUInfo.second.Length + 4);
|
|
Buffer += 16;
|
|
}
|
|
|
|
// Copy over types CU list
|
|
// Spec says " triplet, the first value is the CU offset, the second value is
|
|
// the type offset in the CU, and the third value is the type signature"
|
|
// Looking at what is being generated by gdb-add-index. The first entry is TU
|
|
// offset, second entry is offset from it, and third entry is the type
|
|
// signature.
|
|
memcpy(Buffer, GdbIndexContents.data() + CUTypesOffset,
|
|
AddressTableOffset - CUTypesOffset);
|
|
Buffer += AddressTableOffset - CUTypesOffset;
|
|
|
|
// Generate new address table.
|
|
for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair :
|
|
ARangesSectionWriter->getCUAddressRanges()) {
|
|
const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first];
|
|
const DebugAddressRangesVector &Ranges = CURangesPair.second;
|
|
for (const DebugAddressRange &Range : Ranges) {
|
|
write64le(Buffer, Range.LowPC);
|
|
write64le(Buffer + 8, Range.HighPC);
|
|
write32le(Buffer + 16, CUIndex);
|
|
Buffer += 20;
|
|
}
|
|
}
|
|
|
|
const size_t TrailingSize =
|
|
GdbIndexContents.data() + GdbIndexContents.size() - Data;
|
|
assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize &&
|
|
"size calculation error");
|
|
|
|
// Copy over the rest of the original data.
|
|
memcpy(Buffer, Data, TrailingSize);
|
|
|
|
// Register the new section.
|
|
BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents,
|
|
NewGdbIndexSize);
|
|
}
|
|
|
|
void DWARFRewriter::convertToRanges(DWARFDie DIE,
|
|
const DebugAddressRangesVector &Ranges,
|
|
SimpleBinaryPatcher &DebugInfoPatcher) {
|
|
uint64_t RangesSectionOffset;
|
|
if (Ranges.empty())
|
|
RangesSectionOffset = RangesSectionWriter->getEmptyRangesOffset();
|
|
else
|
|
RangesSectionOffset = RangesSectionWriter->addRanges(Ranges);
|
|
|
|
convertToRangesPatchDebugInfo(DIE, RangesSectionOffset, DebugInfoPatcher);
|
|
}
|
|
|
|
void DWARFRewriter::convertPending(const DWARFUnit &Unit,
|
|
const DWARFAbbreviationDeclaration *Abbrev,
|
|
SimpleBinaryPatcher &DebugInfoPatcher,
|
|
DebugAbbrevWriter &AbbrevWriter) {
|
|
if (ConvertedRangesAbbrevs.count(Abbrev))
|
|
return;
|
|
|
|
convertToRangesPatchAbbrev(Unit, Abbrev, AbbrevWriter);
|
|
|
|
auto I = PendingRanges.find(Abbrev);
|
|
if (I != PendingRanges.end()) {
|
|
for (std::pair<DWARFDieWrapper, DebugAddressRange> &Pair : I->second)
|
|
convertToRanges(Pair.first, {Pair.second}, DebugInfoPatcher);
|
|
PendingRanges.erase(I);
|
|
}
|
|
|
|
ConvertedRangesAbbrevs.emplace(Abbrev);
|
|
}
|
|
|
|
void DWARFRewriter::addToPendingRanges(
|
|
const DWARFAbbreviationDeclaration *Abbrev, DWARFDie DIE,
|
|
DebugAddressRangesVector &FunctionRanges, Optional<uint64_t> DWOId) {
|
|
Optional<DWARFFormValue> LowPcValue = DIE.find(dwarf::DW_AT_low_pc);
|
|
Optional<DWARFFormValue> HighPcValue = DIE.find(dwarf::DW_AT_high_pc);
|
|
if (LowPcValue &&
|
|
LowPcValue->getForm() == dwarf::Form::DW_FORM_GNU_addr_index) {
|
|
assert(DWOId && "Invalid DWO ID.");
|
|
(void)DWOId;
|
|
assert(HighPcValue && "Low PC exists, but not High PC.");
|
|
(void)HighPcValue;
|
|
uint64_t IndexL = LowPcValue->getRawUValue();
|
|
uint64_t IndexH = HighPcValue->getRawUValue();
|
|
for (auto Address : FunctionRanges) {
|
|
AddrWriter->addIndexAddress(Address.LowPC, IndexL, *DWOId);
|
|
// 2.17.2
|
|
// If the value of the DW_AT_high_pc is of class address, it is the
|
|
// relocated address of the first location past the last instruction
|
|
// associated with the entity; if it is of class constant, the value is
|
|
// an unsigned integer offset which when added to the low PC gives the
|
|
// address of the first location past the last instruction associated
|
|
// with the entity.
|
|
if (!HighPcValue->isFormClass(DWARFFormValue::FC_Constant))
|
|
AddrWriter->addIndexAddress(Address.HighPC, IndexH, *DWOId);
|
|
}
|
|
}
|
|
PendingRanges[Abbrev].emplace_back(
|
|
std::make_pair(DWARFDieWrapper(DIE), FunctionRanges.front()));
|
|
}
|
|
|
|
std::unique_ptr<DebugBufferVector>
|
|
DWARFRewriter::makeFinalLocListsSection(SimpleBinaryPatcher &DebugInfoPatcher) {
|
|
auto LocBuffer = std::make_unique<DebugBufferVector>();
|
|
auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
|
|
auto Writer =
|
|
std::unique_ptr<MCObjectWriter>(BC.createObjectWriter(*LocStream));
|
|
|
|
uint64_t SectionOffset = 0;
|
|
|
|
// Add an empty list as the first entry;
|
|
const char Zeroes[16] = {0};
|
|
*LocStream << StringRef(Zeroes, 16);
|
|
SectionOffset += 2 * 8;
|
|
|
|
for (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc :
|
|
LocListWritersByCU) {
|
|
DebugLocWriter *LocWriter = Loc.second.get();
|
|
if (auto *LocListWriter = llvm::dyn_cast<DebugLoclistWriter>(LocWriter)) {
|
|
SimpleBinaryPatcher *Patcher =
|
|
getBinaryDWODebugInfoPatcher(LocListWriter->getDWOID());
|
|
LocListWriter->finalize(0, *Patcher);
|
|
continue;
|
|
}
|
|
LocWriter->finalize(SectionOffset, DebugInfoPatcher);
|
|
std::unique_ptr<DebugBufferVector> CurrCULocationLists =
|
|
LocWriter->getBuffer();
|
|
*LocStream << *CurrCULocationLists;
|
|
SectionOffset += CurrCULocationLists->size();
|
|
}
|
|
|
|
return LocBuffer;
|
|
}
|
|
|
|
void DWARFRewriter::flushPendingRanges(SimpleBinaryPatcher &DebugInfoPatcher) {
|
|
for (std::pair<const DWARFAbbreviationDeclaration *const,
|
|
std::vector<std::pair<DWARFDieWrapper, DebugAddressRange>>>
|
|
&I : PendingRanges)
|
|
for (std::pair<DWARFDieWrapper, DebugAddressRange> &RangePair : I.second)
|
|
patchLowHigh(RangePair.first, RangePair.second, DebugInfoPatcher);
|
|
clearList(PendingRanges);
|
|
}
|
|
|
|
namespace {
|
|
|
|
void getRangeAttrData(DWARFDie DIE, Optional<AttrInfo> &LowPCVal,
|
|
Optional<AttrInfo> &HighPCVal) {
|
|
LowPCVal = findAttributeInfo(DIE, dwarf::DW_AT_low_pc);
|
|
HighPCVal = findAttributeInfo(DIE, dwarf::DW_AT_high_pc);
|
|
uint64_t LowPCOffset = LowPCVal->Offset;
|
|
uint64_t HighPCOffset = HighPCVal->Offset;
|
|
dwarf::Form LowPCForm = LowPCVal->V.getForm();
|
|
dwarf::Form HighPCForm = HighPCVal->V.getForm();
|
|
|
|
if (LowPCForm != dwarf::DW_FORM_addr &&
|
|
LowPCForm != dwarf::DW_FORM_GNU_addr_index) {
|
|
errs() << "BOLT-WARNING: unexpected low_pc form value. Cannot update DIE "
|
|
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n";
|
|
return;
|
|
}
|
|
if (HighPCForm != dwarf::DW_FORM_addr && HighPCForm != dwarf::DW_FORM_data8 &&
|
|
HighPCForm != dwarf::DW_FORM_data4 &&
|
|
HighPCForm != dwarf::DW_FORM_data2 &&
|
|
HighPCForm != dwarf::DW_FORM_data1 &&
|
|
HighPCForm != dwarf::DW_FORM_udata) {
|
|
errs() << "BOLT-WARNING: unexpected high_pc form value. Cannot update DIE "
|
|
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n";
|
|
return;
|
|
}
|
|
if ((LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) &&
|
|
LowPCForm != dwarf::DW_FORM_GNU_addr_index) {
|
|
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
|
|
<< "Cannot update DIE at offset 0x"
|
|
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
|
return;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void DWARFRewriter::patchLowHigh(DWARFDie DIE, DebugAddressRange Range,
|
|
SimpleBinaryPatcher &DebugInfoPatcher) {
|
|
Optional<AttrInfo> LowPCVal = None;
|
|
Optional<AttrInfo> HighPCVal = None;
|
|
getRangeAttrData(DIE, LowPCVal, HighPCVal);
|
|
uint64_t LowPCOffset = LowPCVal->Offset;
|
|
uint64_t HighPCOffset = HighPCVal->Offset;
|
|
auto *TempDebugPatcher = &DebugInfoPatcher;
|
|
if (LowPCVal->V.getForm() == dwarf::DW_FORM_GNU_addr_index) {
|
|
DWARFUnit *Unit = DIE.getDwarfUnit();
|
|
assert(Unit->isDWOUnit() && "DW_FORM_GNU_addr_index not part of DWO.");
|
|
uint32_t AddressIndex =
|
|
AddrWriter->getIndexFromAddress(Range.LowPC, *Unit->getDWOId());
|
|
TempDebugPatcher = getBinaryDWODebugInfoPatcher(*Unit->getDWOId());
|
|
TempDebugPatcher->addUDataPatch(LowPCOffset, AddressIndex,
|
|
std::abs(int(HighPCOffset - LowPCOffset)));
|
|
// TODO: In DWARF5 support ULEB128 for high_pc
|
|
} else {
|
|
TempDebugPatcher->addLE64Patch(LowPCOffset, Range.LowPC);
|
|
}
|
|
|
|
uint64_t HighPC = Range.HighPC;
|
|
// The DW_FORM_data* is delta between high and low pc
|
|
if (HighPCVal->V.getForm() != dwarf::Form::DW_FORM_addr)
|
|
HighPC -= Range.LowPC;
|
|
|
|
if (isHighPcFormEightBytes(HighPCVal->V.getForm()))
|
|
TempDebugPatcher->addLE64Patch(HighPCOffset, HighPC);
|
|
else
|
|
TempDebugPatcher->addLE32Patch(HighPCOffset, HighPC);
|
|
}
|
|
|
|
void DWARFRewriter::convertToRangesPatchAbbrev(
|
|
const DWARFUnit &Unit, const DWARFAbbreviationDeclaration *Abbrev,
|
|
DebugAbbrevWriter &AbbrevWriter, Optional<uint64_t> RangesBase) {
|
|
auto getAttributeForm = [&Abbrev](const dwarf::Attribute Attr) {
|
|
Optional<uint32_t> Index = Abbrev->findAttributeIndex(Attr);
|
|
assert(Index && "attribute not found");
|
|
return Abbrev->getFormByIndex(*Index);
|
|
};
|
|
dwarf::Form LowPCForm = getAttributeForm(dwarf::DW_AT_low_pc);
|
|
|
|
// DW_FORM_GNU_addr_index is already variable encoding so nothing to do
|
|
// there.
|
|
if (RangesBase) {
|
|
assert(LowPCForm != dwarf::DW_FORM_GNU_addr_index);
|
|
AbbrevWriter.addAttributePatch(Unit, Abbrev, dwarf::DW_AT_low_pc,
|
|
dwarf::DW_AT_GNU_ranges_base,
|
|
dwarf::DW_FORM_sec_offset);
|
|
}
|
|
|
|
AbbrevWriter.addAttributePatch(Unit, Abbrev, dwarf::DW_AT_high_pc,
|
|
dwarf::DW_AT_ranges,
|
|
dwarf::DW_FORM_sec_offset);
|
|
}
|
|
|
|
void DWARFRewriter::convertToRangesPatchDebugInfo(
|
|
DWARFDie DIE, uint64_t RangesSectionOffset,
|
|
SimpleBinaryPatcher &DebugInfoPatcher, Optional<uint64_t> RangesBase) {
|
|
Optional<AttrInfo> LowPCVal = None;
|
|
Optional<AttrInfo> HighPCVal = None;
|
|
getRangeAttrData(DIE, LowPCVal, HighPCVal);
|
|
uint64_t LowPCOffset = LowPCVal->Offset;
|
|
uint64_t HighPCOffset = HighPCVal->Offset;
|
|
|
|
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
|
|
uint32_t BaseOffset = 0;
|
|
if (LowPCVal->V.getForm() == dwarf::DW_FORM_GNU_addr_index) {
|
|
// Use ULEB128 for the value.
|
|
DebugInfoPatcher.addUDataPatch(LowPCOffset, 0,
|
|
std::abs(int(HighPCOffset - LowPCOffset)));
|
|
// Ranges are relative to DW_AT_GNU_ranges_base.
|
|
BaseOffset = DebugInfoPatcher.getRangeBase();
|
|
} else {
|
|
// If case DW_AT_low_pc was converted into DW_AT_GNU_ranges_base
|
|
if (RangesBase)
|
|
DebugInfoPatcher.addLE32Patch(LowPCOffset, *RangesBase, 8);
|
|
else
|
|
DebugInfoPatcher.addLE64Patch(LowPCOffset, 0);
|
|
}
|
|
DebugInfoPatcher.addLE32Patch(HighPCOffset, RangesSectionOffset - BaseOffset,
|
|
HighPCVal->Size);
|
|
}
|