blob: 308881081321a23f817e165eb009d15ce303c685 [file] [log] [blame] [edit]
//===- 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/DIEBuilder.h"
#include "bolt/Core/DebugData.h"
#include "bolt/Core/DynoStats.h"
#include "bolt/Core/ParallelUtilities.h"
#include "bolt/Rewrite/RewriteInstance.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DWARFLinker/Classic/DWARFStreamer.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.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/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.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/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt"
static mc::RegisterMCTargetOptionsFlags MOF;
static void printDie(const DWARFDie &DIE) {
DIDumpOptions DumpOpts;
DumpOpts.ShowForm = true;
DumpOpts.Verbose = true;
DumpOpts.ChildRecurseDepth = 0;
DumpOpts.ShowChildren = false;
DIE.dump(dbgs(), 0, DumpOpts);
}
/// Lazily parse DWARF DIE and print it out.
LLVM_ATTRIBUTE_UNUSED
static void printDie(DWARFUnit &DU, uint64_t DIEOffset) {
uint64_t OriginalOffsets = DIEOffset;
uint64_t NextCUOffset = DU.getNextUnitOffset();
DWARFDataExtractor DebugInfoData = DU.getDebugInfoExtractor();
DWARFDebugInfoEntry DIEEntry;
if (DIEEntry.extractFast(DU, &DIEOffset, DebugInfoData, NextCUOffset, 0)) {
if (DIEEntry.getAbbreviationDeclarationPtr()) {
DWARFDie DDie(&DU, &DIEEntry);
printDie(DDie);
} else {
dbgs() << "Failed to extract abbreviation for"
<< Twine::utohexstr(OriginalOffsets) << "\n";
}
} else {
dbgs() << "Failed to extract DIE for " << Twine::utohexstr(OriginalOffsets)
<< " \n";
}
}
using namespace bolt;
/// Take a set of DWARF address ranges corresponding to the input binary and
/// translate them to a set of address ranges in the output binary.
static DebugAddressRangesVector
translateInputToOutputRanges(const BinaryFunction &BF,
const DWARFAddressRangesVector &InputRanges) {
DebugAddressRangesVector OutputRanges;
// If the function hasn't changed return the same ranges.
if (!BF.isEmitted()) {
OutputRanges.resize(InputRanges.size());
llvm::transform(InputRanges, OutputRanges.begin(),
[](const DWARFAddressRange &Range) {
return DebugAddressRange(Range.LowPC, Range.HighPC);
});
return OutputRanges;
}
for (const DWARFAddressRange &Range : InputRanges)
llvm::append_range(OutputRanges, BF.translateInputToOutputRange(
{Range.LowPC, Range.HighPC}));
// Post-processing pass to sort and merge ranges.
llvm::sort(OutputRanges);
DebugAddressRangesVector MergedRanges;
uint64_t PrevHighPC = 0;
for (const DebugAddressRange &Range : OutputRanges) {
if (Range.LowPC <= PrevHighPC) {
MergedRanges.back().HighPC =
std::max(MergedRanges.back().HighPC, Range.HighPC);
} else {
MergedRanges.emplace_back(Range.LowPC, Range.HighPC);
}
PrevHighPC = MergedRanges.back().HighPC;
}
return MergedRanges;
}
/// Similar to translateInputToOutputRanges() but operates on location lists.
static DebugLocationsVector
translateInputToOutputLocationList(const BinaryFunction &BF,
const DebugLocationsVector &InputLL) {
DebugLocationsVector OutputLL;
// If the function hasn't changed - there's nothing to update.
if (!BF.isEmitted())
return InputLL;
for (const DebugLocationEntry &Entry : InputLL) {
DebugAddressRangesVector OutRanges =
BF.translateInputToOutputRange({Entry.LowPC, Entry.HighPC});
if (!OutRanges.empty() && !OutputLL.empty()) {
if (OutRanges.front().LowPC == OutputLL.back().HighPC &&
Entry.Expr == OutputLL.back().Expr) {
OutputLL.back().HighPC =
std::max(OutputLL.back().HighPC, OutRanges.front().HighPC);
OutRanges.erase(OutRanges.begin());
}
}
llvm::transform(OutRanges, std::back_inserter(OutputLL),
[&Entry](const DebugAddressRange &R) {
return DebugLocationEntry{R.LowPC, R.HighPC, Entry.Expr};
});
}
// Sort and merge adjacent entries with identical locations.
llvm::stable_sort(
OutputLL, [](const DebugLocationEntry &A, const DebugLocationEntry &B) {
return A.LowPC < B.LowPC;
});
DebugLocationsVector MergedLL;
uint64_t PrevHighPC = 0;
const SmallVectorImpl<uint8_t> *PrevExpr = nullptr;
for (const DebugLocationEntry &Entry : OutputLL) {
if (Entry.LowPC <= PrevHighPC && *PrevExpr == Entry.Expr) {
MergedLL.back().HighPC = std::max(Entry.HighPC, MergedLL.back().HighPC);
} else {
const uint64_t Begin = std::max(Entry.LowPC, PrevHighPC);
const uint64_t End = std::max(Begin, Entry.HighPC);
MergedLL.emplace_back(DebugLocationEntry{Begin, End, Entry.Expr});
}
PrevHighPC = MergedLL.back().HighPC;
PrevExpr = &MergedLL.back().Expr;
}
return MergedLL;
}
using namespace dwarf_linker;
using namespace dwarf_linker::classic;
namespace llvm {
namespace bolt {
/// Emits debug information into .debug_info or .debug_types section.
class DIEStreamer : public DwarfStreamer {
DIEBuilder *DIEBldr;
GDBIndex &GDBIndexSection;
private:
/// Emit the compilation unit header for \p Unit in the debug_info
/// section.
///
/// A Dwarf 4 section header is encoded as:
/// uint32_t Unit length (omitting this field)
/// uint16_t Version
/// uint32_t Abbreviation table offset
/// uint8_t Address size
/// Leading to a total of 11 bytes.
///
/// A Dwarf 5 section header is encoded as:
/// uint32_t Unit length (omitting this field)
/// uint16_t Version
/// uint8_t Unit type
/// uint8_t Address size
/// uint32_t Abbreviation table offset
/// Leading to a total of 12 bytes.
void emitCompileUnitHeader(DWARFUnit &Unit, DIE &UnitDIE,
unsigned DwarfVersion) {
AsmPrinter &Asm = getAsmPrinter();
switchToDebugInfoSection(DwarfVersion);
emitCommonHeader(Unit, UnitDIE, DwarfVersion);
if (DwarfVersion >= 5 &&
Unit.getUnitType() != dwarf::UnitType::DW_UT_compile) {
std::optional<uint64_t> DWOId = Unit.getDWOId();
assert(DWOId &&
"DWOId does not exist and this is not a DW_UT_compile Unit");
Asm.emitInt64(*DWOId);
}
}
void emitCommonHeader(DWARFUnit &Unit, DIE &UnitDIE, uint16_t Version) {
dwarf::UnitType UT = dwarf::UnitType(Unit.getUnitType());
llvm::AsmPrinter &Asm = getAsmPrinter();
// Emit size of content not including length itself
Asm.emitInt32(Unit.getHeaderSize() + UnitDIE.getSize() - 4);
Asm.emitInt16(Version);
// DWARF v5 reorders the address size and adds a unit type.
if (Version >= 5) {
Asm.emitInt8(UT);
Asm.emitInt8(Asm.MAI->getCodePointerSize());
}
Asm.emitInt32(0);
if (Version <= 4) {
Asm.emitInt8(Asm.MAI->getCodePointerSize());
}
}
void emitTypeUnitHeader(DWARFUnit &Unit, DIE &UnitDIE,
unsigned DwarfVersion) {
AsmPrinter &Asm = getAsmPrinter();
const uint64_t TypeSignature = cast<DWARFTypeUnit>(Unit).getTypeHash();
DIE *TypeDIE = DIEBldr->getTypeDIE(Unit);
const DIEBuilder::DWARFUnitInfo &UI = DIEBldr->getUnitInfoByDwarfUnit(Unit);
GDBIndexSection.addGDBTypeUnitEntry(
{UI.UnitOffset, TypeSignature, TypeDIE->getOffset()});
if (Unit.getVersion() < 5) {
// Switch the section to .debug_types section.
std::unique_ptr<MCStreamer> &MS = Asm.OutStreamer;
llvm::MCContext &MC = Asm.OutContext;
const llvm::MCObjectFileInfo *MOFI = MC.getObjectFileInfo();
MS->switchSection(MOFI->getDwarfTypesSection(0));
MC.setDwarfVersion(DwarfVersion);
} else
switchToDebugInfoSection(DwarfVersion);
emitCommonHeader(Unit, UnitDIE, DwarfVersion);
Asm.OutStreamer->emitIntValue(TypeSignature, sizeof(TypeSignature));
Asm.emitDwarfLengthOrOffset(TypeDIE ? TypeDIE->getOffset() : 0);
}
void emitUnitHeader(DWARFUnit &Unit, DIE &UnitDIE) {
if (Unit.isTypeUnit())
emitTypeUnitHeader(Unit, UnitDIE, Unit.getVersion());
else
emitCompileUnitHeader(Unit, UnitDIE, Unit.getVersion());
}
void emitDIE(DIE &Die) override {
AsmPrinter &Asm = getAsmPrinter();
Asm.emitDwarfDIE(Die);
}
public:
DIEStreamer(DIEBuilder *DIEBldr, GDBIndex &GDBIndexSection,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr),
GDBIndexSection(GDBIndexSection) {};
using DwarfStreamer::emitCompileUnitHeader;
void emitUnit(DWARFUnit &Unit, DIE &UnitDIE) {
emitUnitHeader(Unit, UnitDIE);
emitDIE(UnitDIE);
}
};
/// Finds attributes FormValue and Offset.
///
/// \param DIE die to look up in.
/// \param Attrs finds the first attribute that matches and extracts it.
/// \return an optional AttrInfo with DWARFFormValue and Offset.
std::optional<AttrInfo> findAttributeInfo(const DWARFDie DIE,
std::vector<dwarf::Attribute> Attrs) {
for (dwarf::Attribute &Attr : Attrs)
if (std::optional<AttrInfo> Info = findAttributeInfo(DIE, Attr))
return Info;
return std::nullopt;
}
} // namespace bolt
} // namespace llvm
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::Hidden, cl::cat(BoltCategory));
static cl::opt<unsigned>
DebugThreadCount("debug-thread-count",
cl::desc("specifies thread count for the multithreading "
"for updating DWO debug info"),
cl::init(1), cl::cat(BoltCategory));
static cl::opt<std::string> DwarfOutputPath(
"dwarf-output-path",
cl::desc("Path to where .dwo files will be written out to."), cl::init(""),
cl::cat(BoltCategory));
static cl::opt<bool> CreateDebugNames(
"create-debug-names-section",
cl::desc("Creates .debug_names section, if the input binary doesn't have "
"it already, for DWARF5 CU/TUs."),
cl::init(false), cl::cat(BoltCategory));
static cl::opt<bool>
DebugSkeletonCu("debug-skeleton-cu",
cl::desc("prints out offsets for abbrev and debug_info of "
"Skeleton CUs that get patched."),
cl::ZeroOrMore, cl::Hidden, cl::init(false),
cl::cat(BoltCategory));
static cl::opt<unsigned> BatchSize(
"cu-processing-batch-size",
cl::desc(
"Specifies the size of batches for processing CUs. Higher number has "
"better performance, but more memory usage. Default value is 1."),
cl::Hidden, cl::init(1), cl::cat(BoltCategory));
static cl::opt<bool> AlwaysConvertToRanges(
"always-convert-to-ranges",
cl::desc("This option is for testing purposes only. It forces BOLT to "
"convert low_pc/high_pc to ranges always."),
cl::ReallyHidden, cl::init(false), cl::cat(BoltCategory));
extern cl::opt<std::string> CompDirOverride;
} // namespace opts
/// If DW_AT_low_pc exists sets LowPC and returns true.
static bool getLowPC(const DIE &Die, const DWARFUnit &DU, uint64_t &LowPC,
uint64_t &SectionIndex) {
DIEValue DvalLowPc = Die.findAttribute(dwarf::DW_AT_low_pc);
if (!DvalLowPc)
return false;
dwarf::Form Form = DvalLowPc.getForm();
bool AddrOffset = Form == dwarf::DW_FORM_LLVM_addrx_offset;
uint64_t LowPcValue = DvalLowPc.getDIEInteger().getValue();
if (Form == dwarf::DW_FORM_GNU_addr_index || Form == dwarf::DW_FORM_addrx ||
AddrOffset) {
uint32_t Index = AddrOffset ? (LowPcValue >> 32) : LowPcValue;
std::optional<object::SectionedAddress> SA =
DU.getAddrOffsetSectionItem(Index);
if (!SA)
return false;
if (AddrOffset)
SA->Address += (LowPcValue & 0xffffffff);
LowPC = SA->Address;
SectionIndex = SA->SectionIndex;
} else {
LowPC = LowPcValue;
SectionIndex = 0;
}
return true;
}
/// If DW_AT_high_pc exists sets HighPC and returns true.
static bool getHighPC(const DIE &Die, const uint64_t LowPC, uint64_t &HighPC) {
DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc);
if (!DvalHighPc)
return false;
if (DvalHighPc.getForm() == dwarf::DW_FORM_addr)
HighPC = DvalHighPc.getDIEInteger().getValue();
else
HighPC = LowPC + DvalHighPc.getDIEInteger().getValue();
return true;
}
/// If DW_AT_low_pc and DW_AT_high_pc exist sets LowPC and HighPC and returns
/// true.
static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
uint64_t &LowPC, uint64_t &HighPC,
uint64_t &SectionIndex) {
uint64_t TempLowPC = LowPC;
uint64_t TempHighPC = HighPC;
uint64_t TempSectionIndex = SectionIndex;
if (getLowPC(Die, DU, TempLowPC, TempSectionIndex) &&
getHighPC(Die, TempLowPC, TempHighPC)) {
LowPC = TempLowPC;
HighPC = TempHighPC;
SectionIndex = TempSectionIndex;
return true;
}
return false;
}
static Expected<llvm::DWARFAddressRangesVector>
getDIEAddressRanges(const DIE &Die, DWARFUnit &DU) {
uint64_t LowPC, HighPC, Index;
if (getLowAndHighPC(Die, DU, LowPC, HighPC, Index))
return DWARFAddressRangesVector{{LowPC, HighPC, Index}};
if (DIEValue Dval = Die.findAttribute(dwarf::DW_AT_ranges)) {
if (Dval.getForm() == dwarf::DW_FORM_rnglistx)
return DU.findRnglistFromIndex(Dval.getDIEInteger().getValue());
return DU.findRnglistFromOffset(Dval.getDIEInteger().getValue());
}
return DWARFAddressRangesVector();
}
static std::optional<uint64_t> getAsAddress(const DWARFUnit &DU,
const DIEValue &AttrVal) {
DWARFFormValue::ValueType Value(AttrVal.getDIEInteger().getValue());
if (std::optional<object::SectionedAddress> SA =
DWARFFormValue::getAsSectionedAddress(Value, AttrVal.getForm(), &DU))
return SA->Address;
return std::nullopt;
}
static std::unique_ptr<DIEStreamer>
createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr,
GDBIndex &GDBIndexSection) {
std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, GDBIndexSection, DWARFLinkerBase::OutputFileType::Object,
OutFile,
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
if (Err)
errs()
<< "BOLT-WARNING: [internal-dwarf-error]: Could not init DIEStreamer!"
<< toString(std::move(Err)) << "\n";
return Streamer;
}
static void emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer,
DWARFUnit &Unit) {
DIE *UnitDIE = DIEBldr.getUnitDIEbyUnit(Unit);
Streamer.emitUnit(Unit, *UnitDIE);
}
static void emitDWOBuilder(const std::string &DWOName,
DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter,
DWARFUnit &SplitCU, DWARFUnit &CU,
DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, GDBIndex &GDBIndexSection,
DebugRangesSectionWriter &TempRangesSectionWriter) {
// Populate debug_info and debug_abbrev for current dwo into StringRef.
DWODIEBuilder.generateAbbrevs();
DWODIEBuilder.finish();
SmallVector<char, 20> OutBuffer;
std::shared_ptr<raw_svector_ostream> ObjOS =
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = SplitCU.getContext().getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer =
createDIEStreamer(*TheTriple, *ObjOS, "DwoStreamerInitAug2",
DWODIEBuilder, GDBIndexSection);
if (SplitCU.getContext().getMaxDWOVersion() >= 5) {
for (std::unique_ptr<llvm::DWARFUnit> &CU :
SplitCU.getContext().dwo_info_section_units()) {
if (!CU->isTypeUnit())
continue;
emitUnit(DWODIEBuilder, *Streamer, *CU.get());
}
emitUnit(DWODIEBuilder, *Streamer, SplitCU);
} else {
for (std::unique_ptr<llvm::DWARFUnit> &CU :
SplitCU.getContext().dwo_compile_units())
emitUnit(DWODIEBuilder, *Streamer, *CU.get());
// emit debug_types sections for dwarf4
for (DWARFUnit *CU : DWODIEBuilder.getDWARF4TUVector())
emitUnit(DWODIEBuilder, *Streamer, *CU);
}
Streamer->emitAbbrevs(DWODIEBuilder.getAbbrevs(),
SplitCU.getContext().getMaxVersion());
Streamer->finish();
std::unique_ptr<MemoryBuffer> ObjectMemBuffer =
MemoryBuffer::getMemBuffer(ObjOS->str(), "in-memory object file", false);
std::unique_ptr<object::ObjectFile> Obj = cantFail(
object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()),
"error creating in-memory object");
DWARFRewriter::OverriddenSectionsMap OverriddenSections;
for (const SectionRef &Secs : Obj->sections()) {
StringRef Contents = cantFail(Secs.getContents());
StringRef Name = cantFail(Secs.getName());
DWARFSectionKind Kind =
StringSwitch<DWARFSectionKind>(Name)
.Case(".debug_abbrev", DWARFSectionKind::DW_SECT_ABBREV)
.Case(".debug_info", DWARFSectionKind::DW_SECT_INFO)
.Case(".debug_types", DWARFSectionKind::DW_SECT_EXT_TYPES)
.Default(DWARFSectionKind::DW_SECT_EXT_unknown);
if (Kind == DWARFSectionKind::DW_SECT_EXT_unknown)
continue;
OverriddenSections[Kind] = Contents;
}
Rewriter.writeDWOFiles(CU, OverriddenSections, DWOName, LocWriter,
StrOffstsWriter, StrWriter, TempRangesSectionWriter);
}
using DWARFUnitVec = std::vector<DWARFUnit *>;
using CUPartitionVector = std::vector<DWARFUnitVec>;
/// Partitions CUs in to buckets. Bucket size is controlled by
/// cu-processing-batch-size. All the CUs that have cross CU reference reference
/// as a source are put in to the same initial bucket.
static CUPartitionVector partitionCUs(DWARFContext &DwCtx) {
CUPartitionVector Vec(2);
unsigned Counter = 0;
const DWARFDebugAbbrev *Abbr = DwCtx.getDebugAbbrev();
for (std::unique_ptr<DWARFUnit> &CU : DwCtx.compile_units()) {
Expected<const DWARFAbbreviationDeclarationSet *> AbbrDeclSet =
Abbr->getAbbreviationDeclarationSet(CU->getAbbreviationsOffset());
if (!AbbrDeclSet) {
consumeError(AbbrDeclSet.takeError());
return Vec;
}
bool CrossCURefFound = false;
for (const DWARFAbbreviationDeclaration &Decl : *AbbrDeclSet.get()) {
for (const DWARFAbbreviationDeclaration::AttributeSpec &Attr :
Decl.attributes()) {
if (Attr.Form == dwarf::DW_FORM_ref_addr) {
CrossCURefFound = true;
break;
}
}
if (CrossCURefFound)
break;
}
if (CrossCURefFound) {
Vec[0].push_back(CU.get());
} else {
++Counter;
Vec.back().push_back(CU.get());
}
if (Counter % opts::BatchSize == 0 && !Vec.back().empty())
Vec.push_back({});
}
return Vec;
}
void DWARFRewriter::updateDebugInfo() {
ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
if (!DebugInfo)
return;
ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
StrWriter = std::make_unique<DebugStrWriter>(*BC.DwCtx, false);
StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>(BC);
/// Stores and serializes information that will be put into the
/// .debug_addr DWARF section.
std::unique_ptr<DebugAddrWriter> FinalAddrWriter;
if (BC.isDWARF5Used()) {
FinalAddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC);
RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>();
} else {
FinalAddrWriter = std::make_unique<DebugAddrWriter>(&BC);
}
if (BC.isDWARFLegacyUsed()) {
LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
LegacyRangesSectionWriter->initSection();
}
uint32_t CUIndex = 0;
std::mutex AccessMutex;
// Needs to be invoked in the same order as CUs are processed.
llvm::DenseMap<uint64_t, uint64_t> LocListWritersIndexByCU;
auto createRangeLocListAddressWriters = [&](DWARFUnit &CU) {
std::lock_guard<std::mutex> Lock(AccessMutex);
const uint16_t DwarfVersion = CU.getVersion();
if (DwarfVersion >= 5) {
auto AddrW = std::make_unique<DebugAddrWriterDwarf5>(
&BC, CU.getAddressByteSize(), CU.getAddrOffsetSectionBase());
RangeListsSectionWriter->setAddressWriter(AddrW.get());
LocListWritersByCU[CUIndex] =
std::make_unique<DebugLoclistWriter>(CU, DwarfVersion, false, *AddrW);
if (std::optional<uint64_t> DWOId = CU.getDWOId()) {
assert(RangeListsWritersByCU.count(*DWOId) == 0 &&
"RangeLists writer for DWO unit already exists.");
auto DWORangeListsSectionWriter =
std::make_unique<DebugRangeListsSectionWriter>();
DWORangeListsSectionWriter->initSection(CU);
DWORangeListsSectionWriter->setAddressWriter(AddrW.get());
RangeListsWritersByCU[*DWOId] = std::move(DWORangeListsSectionWriter);
}
AddressWritersByCU[CU.getOffset()] = std::move(AddrW);
} else {
auto AddrW =
std::make_unique<DebugAddrWriter>(&BC, CU.getAddressByteSize());
AddressWritersByCU[CU.getOffset()] = std::move(AddrW);
LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>();
if (std::optional<uint64_t> DWOId = CU.getDWOId()) {
assert(LegacyRangesWritersByCU.count(*DWOId) == 0 &&
"LegacyRangeLists writer for DWO unit already exists.");
auto LegacyRangesSectionWriterByCU =
std::make_unique<DebugRangesSectionWriter>();
LegacyRangesSectionWriterByCU->initSection(CU);
LegacyRangesWritersByCU[*DWOId] =
std::move(LegacyRangesSectionWriterByCU);
}
}
LocListWritersIndexByCU[CU.getOffset()] = CUIndex++;
};
DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
GDBIndex GDBIndexSection(BC);
auto processSplitCU = [&](DWARFUnit &Unit, DWARFUnit &SplitCU,
DebugRangesSectionWriter &TempRangesSectionWriter,
DebugAddrWriter &AddressWriter,
const std::string &DWOName,
const std::optional<std::string> &DwarfOutputPath,
DIEBuilder &DWODIEBuilder) {
DWODIEBuilder.buildDWOUnit(SplitCU);
DebugStrOffsetsWriter DWOStrOffstsWriter(BC);
DebugStrWriter DWOStrWriter((SplitCU).getContext(), true);
DWODIEBuilder.updateDWONameCompDirForTypes(
DWOStrOffstsWriter, DWOStrWriter, SplitCU, DwarfOutputPath, DWOName);
DebugLoclistWriter DebugLocDWoWriter(Unit, Unit.getVersion(), true,
AddressWriter);
updateUnitDebugInfo(SplitCU, DWODIEBuilder, DebugLocDWoWriter,
TempRangesSectionWriter, AddressWriter);
DebugLocDWoWriter.finalize(DWODIEBuilder,
*DWODIEBuilder.getUnitDIEbyUnit(SplitCU));
if (Unit.getVersion() >= 5)
TempRangesSectionWriter.finalizeSection();
emitDWOBuilder(DWOName, DWODIEBuilder, *this, SplitCU, Unit,
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
GDBIndexSection, TempRangesSectionWriter);
};
auto processMainBinaryCU = [&](DWARFUnit &Unit, DIEBuilder &DIEBlder) {
std::optional<DWARFUnit *> SplitCU;
std::optional<uint64_t> RangesBase;
std::optional<uint64_t> DWOId = Unit.getDWOId();
if (DWOId)
SplitCU = BC.getDWOCU(*DWOId);
DebugLocWriter &DebugLocWriter =
*LocListWritersByCU[LocListWritersIndexByCU[Unit.getOffset()]].get();
DebugRangesSectionWriter &RangesSectionWriter =
Unit.getVersion() >= 5 ? *RangeListsSectionWriter.get()
: *LegacyRangesSectionWriter.get();
DebugAddrWriter &AddressWriter =
*AddressWritersByCU[Unit.getOffset()].get();
if (Unit.getVersion() >= 5)
RangeListsSectionWriter->setAddressWriter(&AddressWriter);
if (Unit.getVersion() >= 5) {
RangesBase = RangesSectionWriter.getSectionOffset() +
getDWARF5RngListLocListHeaderSize();
RangesSectionWriter.initSection(Unit);
if (!SplitCU)
StrOffstsWriter->finalizeSection(Unit, DIEBlder);
} else if (SplitCU) {
RangesBase = LegacyRangesSectionWriter.get()->getSectionOffset();
}
updateUnitDebugInfo(Unit, DIEBlder, DebugLocWriter, RangesSectionWriter,
AddressWriter, RangesBase);
DebugLocWriter.finalize(DIEBlder, *DIEBlder.getUnitDIEbyUnit(Unit));
if (Unit.getVersion() >= 5)
RangesSectionWriter.finalizeSection();
};
DIEBuilder DIEBlder(BC, BC.DwCtx.get(), DebugNamesTable);
DIEBlder.buildTypeUnits(StrOffstsWriter.get());
SmallVector<char, 20> OutBuffer;
std::unique_ptr<raw_svector_ostream> ObjOS =
std::make_unique<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
CUOffsetMap OffsetMap =
finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);
CUPartitionVector PartVec = partitionCUs(*BC.DwCtx);
const unsigned int ThreadCount =
std::min(opts::DebugThreadCount, opts::ThreadCount);
for (std::vector<DWARFUnit *> &Vec : PartVec) {
DIEBlder.buildCompileUnits(Vec);
llvm::SmallVector<std::unique_ptr<DIEBuilder>, 72> DWODIEBuildersByCU;
ThreadPoolInterface &ThreadPool =
ParallelUtilities::getThreadPool(ThreadCount);
for (DWARFUnit *CU : DIEBlder.getProcessedCUs()) {
createRangeLocListAddressWriters(*CU);
std::optional<DWARFUnit *> SplitCU;
std::optional<uint64_t> DWOId = CU->getDWOId();
if (DWOId)
SplitCU = BC.getDWOCU(*DWOId);
if (!SplitCU)
continue;
DebugAddrWriter &AddressWriter =
*AddressWritersByCU[CU->getOffset()].get();
DebugRangesSectionWriter &TempRangesSectionWriter =
CU->getVersion() >= 5 ? *RangeListsWritersByCU[*DWOId].get()
: *LegacyRangesWritersByCU[*DWOId].get();
std::optional<std::string> DwarfOutputPath =
opts::DwarfOutputPath.empty()
? std::nullopt
: std::optional<std::string>(opts::DwarfOutputPath.c_str());
std::string DWOName = DIEBlder.updateDWONameCompDir(
*StrOffstsWriter, *StrWriter, *CU, DwarfOutputPath, std::nullopt);
auto DWODIEBuilderPtr = std::make_unique<DIEBuilder>(
BC, &(**SplitCU).getContext(), DebugNamesTable, CU);
DIEBuilder &DWODIEBuilder =
*DWODIEBuildersByCU.emplace_back(std::move(DWODIEBuilderPtr)).get();
if (CU->getVersion() >= 5)
StrOffstsWriter->finalizeSection(*CU, DIEBlder);
// Important to capture CU and SplitCU by value here, otherwise when the
// thread is executed at some point after the current iteration of the
// loop, dereferencing CU/SplitCU in the call to processSplitCU means it
// will dereference a different variable than the one intended, causing a
// seg fault.
ThreadPool.async([&, DwarfOutputPath, DWOName, CU, SplitCU] {
processSplitCU(*CU, **SplitCU, TempRangesSectionWriter, AddressWriter,
DWOName, DwarfOutputPath, DWODIEBuilder);
});
}
ThreadPool.wait();
for (std::unique_ptr<DIEBuilder> &DWODIEBuilderPtr : DWODIEBuildersByCU)
DWODIEBuilderPtr->updateDebugNamesTable();
for (DWARFUnit *CU : DIEBlder.getProcessedCUs())
processMainBinaryCU(*CU, DIEBlder);
finalizeCompileUnits(DIEBlder, *Streamer, OffsetMap,
DIEBlder.getProcessedCUs(), *FinalAddrWriter);
}
DebugNamesTable.emitAccelTable();
finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS, OffsetMap,
*FinalAddrWriter);
GDBIndexSection.updateGdbIndexSection(OffsetMap, CUIndex,
*ARangesSectionWriter);
}
void DWARFRewriter::updateUnitDebugInfo(
DWARFUnit &Unit, DIEBuilder &DIEBldr, DebugLocWriter &DebugLocWriter,
DebugRangesSectionWriter &RangesSectionWriter,
DebugAddrWriter &AddressWriter, std::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();
const std::vector<std::unique_ptr<DIEBuilder::DIEInfo>> &DIs =
DIEBldr.getDIEsByUnit(Unit);
// Either updates or normalizes DW_AT_range to DW_AT_low_pc and DW_AT_high_pc.
auto updateLowPCHighPC = [&](DIE *Die, const DIEValue &LowPCVal,
const DIEValue &HighPCVal, uint64_t LowPC,
const uint64_t HighPC) {
dwarf::Attribute AttrLowPC = dwarf::DW_AT_low_pc;
dwarf::Form FormLowPC = dwarf::DW_FORM_addr;
dwarf::Attribute AttrHighPC = dwarf::DW_AT_high_pc;
dwarf::Form FormHighPC = dwarf::DW_FORM_data4;
const uint32_t Size = HighPC - LowPC;
// Whatever was generated is not low_pc/high_pc, so will reset to
// default for size 1.
if (!LowPCVal || !HighPCVal) {
if (Unit.getVersion() >= 5)
FormLowPC = dwarf::DW_FORM_addrx;
else if (Unit.isDWOUnit())
FormLowPC = dwarf::DW_FORM_GNU_addr_index;
} else {
AttrLowPC = LowPCVal.getAttribute();
FormLowPC = LowPCVal.getForm();
AttrHighPC = HighPCVal.getAttribute();
FormHighPC = HighPCVal.getForm();
}
if (FormLowPC == dwarf::DW_FORM_addrx ||
FormLowPC == dwarf::DW_FORM_GNU_addr_index)
LowPC = AddressWriter.getIndexFromAddress(LowPC, Unit);
if (LowPCVal)
DIEBldr.replaceValue(Die, AttrLowPC, FormLowPC, DIEInteger(LowPC));
else
DIEBldr.addValue(Die, AttrLowPC, FormLowPC, DIEInteger(LowPC));
if (HighPCVal) {
DIEBldr.replaceValue(Die, AttrHighPC, FormHighPC, DIEInteger(Size));
} else {
DIEBldr.deleteValue(Die, dwarf::DW_AT_ranges);
DIEBldr.addValue(Die, AttrHighPC, FormHighPC, DIEInteger(Size));
}
};
for (const std::unique_ptr<DIEBuilder::DIEInfo> &DI : DIs) {
DIE *Die = DI->Die;
switch (Die->getTag()) {
case dwarf::DW_TAG_compile_unit:
case dwarf::DW_TAG_skeleton_unit: {
// For dwarf5 section 3.1.3
// The following attributes are not part of a split full compilation unit
// entry but instead are inherited (if present) from the corresponding
// skeleton compilation unit: DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges,
// DW_AT_stmt_list, DW_AT_comp_dir, DW_AT_str_offsets_base,
// DW_AT_addr_base and DW_AT_rnglists_base.
if (Unit.getVersion() == 5 && Unit.isDWOUnit())
continue;
auto ModuleRangesOrError = getDIEAddressRanges(*Die, Unit);
if (!ModuleRangesOrError) {
consumeError(ModuleRangesOrError.takeError());
break;
}
DWARFAddressRangesVector &ModuleRanges = *ModuleRangesOrError;
DebugAddressRangesVector OutputRanges =
BC.translateModuleAddressRanges(ModuleRanges);
DIEValue LowPCAttrInfo = Die->findAttribute(dwarf::DW_AT_low_pc);
// For a case where LLD GCs only function used in the CU.
// If CU doesn't have DW_AT_low_pc we are not going to convert,
// so don't need to do anything.
if (OutputRanges.empty() && !Unit.isDWOUnit() && LowPCAttrInfo)
OutputRanges.push_back({0, 0});
const uint64_t RangesSectionOffset =
RangesSectionWriter.addRanges(OutputRanges);
// Don't emit the zero low_pc arange.
if (!Unit.isDWOUnit() && !OutputRanges.empty() &&
OutputRanges.back().LowPC)
ARangesSectionWriter->addCURanges(Unit.getOffset(),
std::move(OutputRanges));
updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset,
RangesBase);
DIEValue StmtListAttrVal = Die->findAttribute(dwarf::DW_AT_stmt_list);
if (LineTablePatchMap.count(&Unit))
DIEBldr.replaceValue(Die, dwarf::DW_AT_stmt_list,
StmtListAttrVal.getForm(),
DIEInteger(LineTablePatchMap[&Unit]));
break;
}
case dwarf::DW_TAG_subprogram: {
// Get function address either from ranges or [LowPC, HighPC) pair.
uint64_t Address = UINT64_MAX;
uint64_t SectionIndex, HighPC;
DebugAddressRangesVector FunctionRanges;
if (!getLowAndHighPC(*Die, Unit, Address, HighPC, SectionIndex)) {
Expected<DWARFAddressRangesVector> RangesOrError =
getDIEAddressRanges(*Die, Unit);
if (!RangesOrError) {
consumeError(RangesOrError.takeError());
break;
}
DWARFAddressRangesVector Ranges = *RangesOrError;
// Not a function definition.
if (Ranges.empty())
break;
for (const DWARFAddressRange &Range : Ranges) {
if (const BinaryFunction *Function =
BC.getBinaryFunctionAtAddress(Range.LowPC))
FunctionRanges.append(Function->getOutputAddressRanges());
}
} else {
if (const BinaryFunction *Function =
BC.getBinaryFunctionAtAddress(Address))
FunctionRanges = Function->getOutputAddressRanges();
}
// Clear cached ranges as the new function will have its own set.
CachedRanges.clear();
DIEValue LowPCVal = Die->findAttribute(dwarf::DW_AT_low_pc);
DIEValue HighPCVal = Die->findAttribute(dwarf::DW_AT_high_pc);
if (FunctionRanges.empty()) {
if (LowPCVal && HighPCVal)
FunctionRanges.push_back({0, HighPCVal.getDIEInteger().getValue()});
else
FunctionRanges.push_back({0, 1});
}
if (FunctionRanges.size() == 1 && !opts::AlwaysConvertToRanges) {
updateLowPCHighPC(Die, LowPCVal, HighPCVal, FunctionRanges.back().LowPC,
FunctionRanges.back().HighPC);
break;
}
updateDWARFObjectAddressRanges(
Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges));
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 = 0;
Expected<DWARFAddressRangesVector> RangesOrError =
getDIEAddressRanges(*Die, Unit);
const BinaryFunction *Function =
RangesOrError && !RangesOrError->empty()
? BC.getBinaryFunctionContainingAddress(
RangesOrError->front().LowPC)
: nullptr;
DebugAddressRangesVector OutputRanges;
if (Function) {
OutputRanges = translateInputToOutputRanges(*Function, *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';
});
if (opts::AlwaysConvertToRanges || OutputRanges.size() > 1) {
RangesSectionOffset = RangesSectionWriter.addRanges(
std::move(OutputRanges), CachedRanges);
OutputRanges.clear();
} else if (OutputRanges.empty()) {
OutputRanges.push_back({0, RangesOrError.get().front().HighPC});
}
} else if (!RangesOrError) {
consumeError(RangesOrError.takeError());
} else {
OutputRanges.push_back({0, !RangesOrError->empty()
? RangesOrError.get().front().HighPC
: 0});
}
DIEValue LowPCVal = Die->findAttribute(dwarf::DW_AT_low_pc);
DIEValue HighPCVal = Die->findAttribute(dwarf::DW_AT_high_pc);
if (OutputRanges.size() == 1) {
updateLowPCHighPC(Die, LowPCVal, HighPCVal, OutputRanges.back().LowPC,
OutputRanges.back().HighPC);
break;
}
updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset);
break;
}
case dwarf::DW_TAG_call_site: {
auto patchPC = [&](DIE *Die, DIEValue &AttrVal, StringRef Entry) -> void {
std::optional<uint64_t> Address = getAsAddress(Unit, AttrVal);
const BinaryFunction *Function =
BC.getBinaryFunctionContainingAddress(*Address);
uint64_t UpdatedAddress = *Address;
if (Function)
UpdatedAddress =
Function->translateInputToOutputAddress(UpdatedAddress);
if (AttrVal.getForm() == dwarf::DW_FORM_addrx) {
const uint32_t Index =
AddressWriter.getIndexFromAddress(UpdatedAddress, Unit);
DIEBldr.replaceValue(Die, AttrVal.getAttribute(), AttrVal.getForm(),
DIEInteger(Index));
} else if (AttrVal.getForm() == dwarf::DW_FORM_addr) {
DIEBldr.replaceValue(Die, AttrVal.getAttribute(), AttrVal.getForm(),
DIEInteger(UpdatedAddress));
} else {
errs() << "BOLT-ERROR: unsupported form for " << Entry << "\n";
}
};
DIEValue CallPcAttrVal = Die->findAttribute(dwarf::DW_AT_call_pc);
if (CallPcAttrVal)
patchPC(Die, CallPcAttrVal, "DW_AT_call_pc");
DIEValue CallRetPcAttrVal =
Die->findAttribute(dwarf::DW_AT_call_return_pc);
if (CallRetPcAttrVal)
patchPC(Die, CallRetPcAttrVal, "DW_AT_call_return_pc");
break;
}
default: {
// Handle any tag that can have DW_AT_location attribute.
DIEValue LocAttrInfo = Die->findAttribute(dwarf::DW_AT_location);
DIEValue LowPCAttrInfo = Die->findAttribute(dwarf::DW_AT_low_pc);
if (LocAttrInfo) {
if (doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_Constant,
Unit.getVersion()) ||
doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_SectionOffset,
Unit.getVersion())) {
uint64_t Offset = LocAttrInfo.getForm() == dwarf::DW_FORM_loclistx
? LocAttrInfo.getDIELocList().getValue()
: LocAttrInfo.getDIEInteger().getValue();
DebugLocationsVector InputLL;
std::optional<object::SectionedAddress> SectionAddress =
Unit.getBaseAddress();
uint64_t BaseAddress = 0;
if (SectionAddress)
BaseAddress = SectionAddress->Address;
if (Unit.getVersion() >= 5 &&
LocAttrInfo.getForm() == dwarf::DW_FORM_loclistx) {
std::optional<uint64_t> LocOffset = Unit.getLoclistOffset(Offset);
assert(LocOffset && "Location Offset is invalid.");
Offset = *LocOffset;
}
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() || Unit.getVersion() == 5)) &&
"absolute address expected");
InputLL.emplace_back(DebugLocationEntry{
BaseAddress + Entry.Value0, BaseAddress + Entry.Value1,
Entry.Loc});
break;
case dwarf::DW_LLE_start_length:
InputLL.emplace_back(DebugLocationEntry{
Entry.Value0, Entry.Value0 + Entry.Value1, Entry.Loc});
break;
case dwarf::DW_LLE_base_addressx: {
std::optional<object::SectionedAddress> EntryAddress =
Unit.getAddrOffsetSectionItem(Entry.Value0);
assert(EntryAddress && "base Address not found.");
BaseAddress = EntryAddress->Address;
break;
}
case dwarf::DW_LLE_startx_length: {
std::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;
}
case dwarf::DW_LLE_startx_endx: {
std::optional<object::SectionedAddress> StartAddress =
Unit.getAddrOffsetSectionItem(Entry.Value0);
assert(StartAddress && "Start Address does not exist.");
std::optional<object::SectionedAddress> EndAddress =
Unit.getAddrOffsetSectionItem(Entry.Value1);
assert(EndAddress && "Start Address does not exist.");
InputLL.emplace_back(DebugLocationEntry{
StartAddress->Address, EndAddress->Address, Entry.Loc});
break;
}
}
return true;
});
if (E || InputLL.empty()) {
consumeError(std::move(E));
errs() << "BOLT-WARNING: empty location list detected at 0x"
<< Twine::utohexstr(Offset) << " for DIE at 0x" << Die
<< " in CU at 0x" << Twine::utohexstr(Unit.getOffset())
<< '\n';
} else {
const uint64_t Address = InputLL.front().LowPC;
DebugLocationsVector OutputLL;
if (const BinaryFunction *Function =
BC.getBinaryFunctionContainingAddress(Address)) {
OutputLL = translateInputToOutputLocationList(*Function, InputLL);
LLVM_DEBUG(if (OutputLL.empty()) {
dbgs() << "BOLT-DEBUG: location list translated to an empty "
"one at 0x"
<< Die << " in CU at 0x"
<< Twine::utohexstr(Unit.getOffset()) << '\n';
});
} else {
// It's possible for a subprogram to be removed and to have
// address of 0. Adding this entry to output to preserve debug
// information.
OutputLL = InputLL;
}
DebugLocWriter.addList(DIEBldr, *Die, LocAttrInfo, OutputLL);
}
} else {
assert((doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_Exprloc,
Unit.getVersion()) ||
doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_Block,
Unit.getVersion())) &&
"unexpected DW_AT_location form");
if (Unit.isDWOUnit() || Unit.getVersion() >= 5) {
std::vector<uint8_t> Sblock;
DIEValueList *AttrLocValList;
if (doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_Exprloc,
Unit.getVersion())) {
for (const DIEValue &Val : LocAttrInfo.getDIELoc().values()) {
Sblock.push_back(Val.getDIEInteger().getValue());
}
DIELoc *LocAttr = const_cast<DIELoc *>(&LocAttrInfo.getDIELoc());
AttrLocValList = static_cast<DIEValueList *>(LocAttr);
} else {
for (const DIEValue &Val : LocAttrInfo.getDIEBlock().values()) {
Sblock.push_back(Val.getDIEInteger().getValue());
}
DIEBlock *BlockAttr =
const_cast<DIEBlock *>(&LocAttrInfo.getDIEBlock());
AttrLocValList = static_cast<DIEValueList *>(BlockAttr);
}
ArrayRef<uint8_t> Expr = ArrayRef<uint8_t>(Sblock);
DataExtractor Data(
StringRef((const char *)Expr.data(), Expr.size()),
Unit.getContext().isLittleEndian(), 0);
DWARFExpression LocExpr(Data, Unit.getAddressByteSize(),
Unit.getFormParams().Format);
uint32_t PrevOffset = 0;
DIEValueList *NewAttr;
DIEValue Value;
uint32_t NewExprSize = 0;
DIELoc *Loc = nullptr;
DIEBlock *Block = nullptr;
if (LocAttrInfo.getForm() == dwarf::DW_FORM_exprloc) {
Loc = DIEBldr.allocateDIEValue<DIELoc>();
NewAttr = Loc;
Value = DIEValue(LocAttrInfo.getAttribute(),
LocAttrInfo.getForm(), Loc);
} else if (doesFormBelongToClass(LocAttrInfo.getForm(),
DWARFFormValue::FC_Block,
Unit.getVersion())) {
Block = DIEBldr.allocateDIEValue<DIEBlock>();
NewAttr = Block;
Value = DIEValue(LocAttrInfo.getAttribute(),
LocAttrInfo.getForm(), Block);
} else {
errs() << "BOLT-WARNING: Unexpected Form value in Updating "
"DW_AT_Location\n";
continue;
}
for (const DWARFExpression::Operation &Expr : LocExpr) {
uint32_t CurEndOffset = PrevOffset + 1;
if (Expr.getDescription().Op.size() == 1)
CurEndOffset = Expr.getOperandEndOffset(0);
if (Expr.getDescription().Op.size() == 2)
CurEndOffset = Expr.getOperandEndOffset(1);
if (Expr.getDescription().Op.size() > 2)
errs() << "BOLT-WARNING: [internal-dwarf-error]: Unsupported "
"number of operands.\n";
// not addr index, just copy.
if (!(Expr.getCode() == dwarf::DW_OP_GNU_addr_index ||
Expr.getCode() == dwarf::DW_OP_addrx)) {
auto Itr = AttrLocValList->values().begin();
std::advance(Itr, PrevOffset);
uint32_t CopyNum = CurEndOffset - PrevOffset;
NewExprSize += CopyNum;
while (CopyNum--) {
DIEBldr.addValue(NewAttr, *Itr);
std::advance(Itr, 1);
}
} else {
const uint64_t Index = Expr.getRawOperand(0);
std::optional<object::SectionedAddress> EntryAddress =
Unit.getAddrOffsetSectionItem(Index);
assert(EntryAddress && "Address is not found.");
assert(Index <= std::numeric_limits<uint32_t>::max() &&
"Invalid Operand Index.");
const uint32_t AddrIndex = AddressWriter.getIndexFromAddress(
EntryAddress->Address, Unit);
// update Index into .debug_address section for DW_AT_location.
// The Size field is not stored in IR, we need to minus 1 in
// offset for each expr.
SmallString<8> Tmp;
raw_svector_ostream OSE(Tmp);
encodeULEB128(AddrIndex, OSE);
DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0),
dwarf::DW_FORM_data1,
DIEInteger(Expr.getCode()));
NewExprSize += 1;
for (uint8_t Byte : Tmp) {
DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0),
dwarf::DW_FORM_data1, DIEInteger(Byte));
NewExprSize += 1;
}
}
PrevOffset = CurEndOffset;
}
// update the size since the index might be changed
if (Loc)
Loc->setSize(NewExprSize);
else
Block->setSize(NewExprSize);
DIEBldr.replaceValue(Die, LocAttrInfo.getAttribute(),
LocAttrInfo.getForm(), Value);
}
}
} else if (LowPCAttrInfo) {
uint64_t Address = 0;
uint64_t SectionIndex = 0;
if (getLowPC(*Die, Unit, Address, SectionIndex)) {
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 = LowPCAttrInfo.getForm();
assert(Form != dwarf::DW_FORM_LLVM_addrx_offset &&
"DW_FORM_LLVM_addrx_offset is not supported");
std::lock_guard<std::mutex> Lock(DWARFRewriterMutex);
if (Form == dwarf::DW_FORM_addrx ||
Form == dwarf::DW_FORM_GNU_addr_index) {
const uint32_t Index = AddressWriter.getIndexFromAddress(
NewAddress ? NewAddress : Address, Unit);
DIEBldr.replaceValue(Die, LowPCAttrInfo.getAttribute(),
LowPCAttrInfo.getForm(), DIEInteger(Index));
} else {
DIEBldr.replaceValue(Die, LowPCAttrInfo.getAttribute(),
LowPCAttrInfo.getForm(),
DIEInteger(NewAddress));
}
} else if (opts::Verbosity >= 1) {
errs() << "BOLT-WARNING: unexpected form value for attribute "
"LowPCAttrInfo\n";
}
}
}
}
}
if (DIEOffset > NextCUOffset)
errs() << "BOLT-WARNING: corrupt DWARF detected at 0x"
<< Twine::utohexstr(Unit.getOffset()) << '\n';
}
void DWARFRewriter::updateDWARFObjectAddressRanges(
DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die, uint64_t DebugRangesOffset,
std::optional<uint64_t> RangesBase) {
if (RangesBase) {
// If DW_AT_GNU_ranges_base is present, update it. No further modifications
// are needed for ranges base.
DIEValue RangesBaseInfo = Die.findAttribute(dwarf::DW_AT_GNU_ranges_base);
if (!RangesBaseInfo) {
RangesBaseInfo = Die.findAttribute(dwarf::DW_AT_rnglists_base);
}
if (RangesBaseInfo) {
if (RangesBaseInfo.getAttribute() == dwarf::DW_AT_GNU_ranges_base) {
auto RangesWriterIterator =
LegacyRangesWritersByCU.find(*Unit.getDWOId());
assert(RangesWriterIterator != LegacyRangesWritersByCU.end() &&
"RangesWriter does not exist for DWOId");
RangesWriterIterator->second->setDie(&Die);
} else {
DIEBldr.replaceValue(&Die, RangesBaseInfo.getAttribute(),
RangesBaseInfo.getForm(),
DIEInteger(static_cast<uint32_t>(*RangesBase)));
}
RangesBase = std::nullopt;
}
}
DIEValue LowPCAttrInfo = Die.findAttribute(dwarf::DW_AT_low_pc);
DIEValue RangesAttrInfo = Die.findAttribute(dwarf::DW_AT_ranges);
if (RangesAttrInfo) {
// 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.
// For DWARF5 converting all of DW_AT_ranges into DW_FORM_rnglistx
bool NeedConverted = false;
if (Unit.getVersion() >= 5 &&
RangesAttrInfo.getForm() == dwarf::DW_FORM_sec_offset)
NeedConverted = true;
if (NeedConverted || RangesAttrInfo.getForm() == dwarf::DW_FORM_rnglistx)
DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, dwarf::DW_FORM_rnglistx,
DIEInteger(DebugRangesOffset));
else
DIEBldr.replaceValue(&Die, dwarf::DW_AT_ranges, RangesAttrInfo.getForm(),
DIEInteger(DebugRangesOffset));
if (!RangesBase) {
if (LowPCAttrInfo &&
LowPCAttrInfo.getForm() != dwarf::DW_FORM_GNU_addr_index &&
LowPCAttrInfo.getForm() != dwarf::DW_FORM_addrx)
DIEBldr.replaceValue(&Die, dwarf::DW_AT_low_pc, LowPCAttrInfo.getForm(),
DIEInteger(0));
return;
}
if (!(Die.getTag() == dwarf::DW_TAG_compile_unit ||
Die.getTag() == dwarf::DW_TAG_skeleton_unit))
return;
// If we are at this point we are in the CU/Skeleton CU, and
// DW_AT_GNU_ranges_base or DW_AT_rnglists_base doesn't exist.
if (Unit.getVersion() <= 4) {
DIEBldr.addValue(&Die, dwarf::DW_AT_GNU_ranges_base, dwarf::DW_FORM_data4,
DIEInteger(INT_MAX));
auto RangesWriterIterator =
LegacyRangesWritersByCU.find(*Unit.getDWOId());
assert(RangesWriterIterator != LegacyRangesWritersByCU.end() &&
"RangesWriter does not exist for DWOId");
RangesWriterIterator->second->setDie(&Die);
} else if (Unit.getVersion() >= 5) {
DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base,
dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase));
}
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.
DIEValue HighPCAttrInfo = Die.findAttribute(dwarf::DW_AT_high_pc);
if (LowPCAttrInfo && HighPCAttrInfo) {
convertToRangesPatchDebugInfo(Unit, DIEBldr, Die, DebugRangesOffset,
LowPCAttrInfo, HighPCAttrInfo, RangesBase);
} else if (!(Unit.isDWOUnit() &&
Die.getTag() == dwarf::DW_TAG_compile_unit)) {
if (opts::Verbosity >= 1)
errs() << "BOLT-WARNING: cannot update ranges for DIE in Unit offset 0x"
<< Twine::utohexstr(Unit.getOffset()) << '\n';
}
}
void DWARFRewriter::updateLineTableOffsets(const MCAssembler &Asm) {
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.");
// 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 =
[](const DWARFDie &DIE) -> std::optional<uint64_t> {
std::optional<DWARFFormValue> StmtList = DIE.find(dwarf::DW_AT_stmt_list);
if (!StmtList)
return std::nullopt;
std::optional<uint64_t> Offset = dwarf::toSectionOffset(StmtList);
assert(Offset && "Was not able to retrieve value of DW_AT_stmt_list.");
return *Offset;
};
SmallVector<DWARFUnit *, 1> TUs;
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->info_section_units()) {
if (CU->isTypeUnit()) {
TUs.push_back(CU.get());
continue;
}
const unsigned CUID = CU->getOffset();
MCSymbol *Label = BC.getDwarfLineTable(CUID).getLabel();
if (!Label)
continue;
std::optional<uint64_t> StmtOffset =
GetStatementListValue(CU.get()->getUnitDIE());
if (!StmtOffset)
continue;
const uint64_t LineTableOffset =
Asm.getSymbolOffset(*Label);
DebugLineOffsetMap[*StmtOffset] = LineTableOffset;
assert(DbgInfoSection && ".debug_info section must exist");
LineTablePatchMap[CU.get()] = LineTableOffset;
}
for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units())
TUs.push_back(TU.get());
for (DWARFUnit *TU : TUs) {
std::optional<uint64_t> StmtOffset =
GetStatementListValue(TU->getUnitDIE());
if (!StmtOffset)
continue;
auto Iter = DebugLineOffsetMap.find(*StmtOffset);
if (Iter == DebugLineOffsetMap.end()) {
// Implementation depends on TU sharing DW_AT_stmt_list with a CU.
// Only case that it hasn't been true was for manually modified assembly
// file. Adding this warning in case assumption is false.
errs()
<< "BOLT-WARNING: [internal-dwarf-error]: A TU at offset: 0x"
<< Twine::utohexstr(TU->getOffset())
<< " is not sharing "
".debug_line entry with CU. DW_AT_stmt_list for this TU won't be "
"updated.\n";
continue;
}
TypeUnitRelocMap[TU] = Iter->second;
}
// 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::finalizeTypeSections(DIEBuilder &DIEBlder,
DIEStreamer &Streamer,
GDBIndex &GDBIndexSection) {
// update TypeUnit DW_AT_stmt_list with new .debug_line information.
auto updateLineTable = [&](const DWARFUnit &Unit) -> void {
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(Unit);
DIEValue StmtAttrInfo = UnitDIE->findAttribute(dwarf::DW_AT_stmt_list);
if (!StmtAttrInfo || !TypeUnitRelocMap.count(&Unit))
return;
DIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_stmt_list,
StmtAttrInfo.getForm(),
DIEInteger(TypeUnitRelocMap[&Unit]));
};
// generate and populate abbrevs here
DIEBlder.generateAbbrevs();
DIEBlder.finish();
DIEBlder.updateDebugNamesTable();
SmallVector<char, 20> OutBuffer;
std::shared_ptr<raw_svector_ostream> ObjOS =
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> TypeStreamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, GDBIndexSection);
// generate debug_info and CUMap
CUOffsetMap CUMap;
for (std::unique_ptr<llvm::DWARFUnit> &CU : BC.DwCtx->info_section_units()) {
if (!CU->isTypeUnit())
continue;
updateLineTable(*CU.get());
emitUnit(DIEBlder, Streamer, *CU.get());
uint32_t StartOffset = CUOffset;
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU.get());
CUOffset += CU.get()->getHeaderSize();
CUOffset += UnitDIE->getSize();
CUMap[CU.get()->getOffset()] = {StartOffset, CUOffset - StartOffset - 4};
}
// Emit Type Unit of DWARF 4 to .debug_type section
for (DWARFUnit *TU : DIEBlder.getDWARF4TUVector()) {
updateLineTable(*TU);
emitUnit(DIEBlder, *TypeStreamer, *TU);
}
TypeStreamer->finish();
std::unique_ptr<MemoryBuffer> ObjectMemBuffer =
MemoryBuffer::getMemBuffer(ObjOS->str(), "in-memory object file", false);
std::unique_ptr<object::ObjectFile> Obj = cantFail(
object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()),
"error creating in-memory object");
for (const SectionRef &Section : Obj->sections()) {
StringRef Contents = cantFail(Section.getContents());
StringRef Name = cantFail(Section.getName());
if (Name == ".debug_types")
BC.registerOrUpdateNoteSection(".debug_types", copyByteArray(Contents),
Contents.size());
}
return CUMap;
}
void DWARFRewriter::finalizeDebugSections(
DIEBuilder &DIEBlder, DWARF5AcceleratorTable &DebugNamesTable,
DIEStreamer &Streamer, raw_svector_ostream &ObjOS, CUOffsetMap &CUMap,
DebugAddrWriter &FinalAddrWriter) {
if (StrWriter->isInitialized()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
StrWriter->releaseBuffer();
BC.registerOrUpdateNoteSection(".debug_str",
copyByteArray(*DebugStrSectionContents),
DebugStrSectionContents->size());
}
if (StrOffstsWriter->isFinalized()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str_offsets");
std::unique_ptr<DebugStrOffsetsBufferVector>
DebugStrOffsetsSectionContents = StrOffstsWriter->releaseBuffer();
BC.registerOrUpdateNoteSection(
".debug_str_offsets", copyByteArray(*DebugStrOffsetsSectionContents),
DebugStrOffsetsSectionContents->size());
}
if (BC.isDWARFLegacyUsed()) {
std::unique_ptr<DebugBufferVector> RangesSectionContents =
LegacyRangesSectionWriter->releaseBuffer();
BC.registerOrUpdateNoteSection(".debug_ranges",
copyByteArray(*RangesSectionContents),
RangesSectionContents->size());
}
if (BC.isDWARF5Used()) {
std::unique_ptr<DebugBufferVector> RangesSectionContents =
RangeListsSectionWriter->releaseBuffer();
BC.registerOrUpdateNoteSection(".debug_rnglists",
copyByteArray(*RangesSectionContents),
RangesSectionContents->size());
}
if (BC.isDWARF5Used()) {
std::unique_ptr<DebugBufferVector> LocationListSectionContents =
makeFinalLocListsSection(DWARFVersion::DWARF5);
if (!LocationListSectionContents->empty())
BC.registerOrUpdateNoteSection(
".debug_loclists", copyByteArray(*LocationListSectionContents),
LocationListSectionContents->size());
}
if (BC.isDWARFLegacyUsed()) {
std::unique_ptr<DebugBufferVector> LocationListSectionContents =
makeFinalLocListsSection(DWARFVersion::DWARFLegacy);
if (!LocationListSectionContents->empty())
BC.registerOrUpdateNoteSection(
".debug_loc", copyByteArray(*LocationListSectionContents),
LocationListSectionContents->size());
}
if (FinalAddrWriter.isInitialized()) {
std::unique_ptr<AddressSectionBuffer> AddressSectionContents =
FinalAddrWriter.releaseBuffer();
BC.registerOrUpdateNoteSection(".debug_addr",
copyByteArray(*AddressSectionContents),
AddressSectionContents->size());
}
Streamer.emitAbbrevs(DIEBlder.getAbbrevs(), BC.DwCtx->getMaxVersion());
Streamer.finish();
std::unique_ptr<MemoryBuffer> ObjectMemBuffer =
MemoryBuffer::getMemBuffer(ObjOS.str(), "in-memory object file", false);
std::unique_ptr<object::ObjectFile> Obj = cantFail(
object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()),
"error creating in-memory object");
for (const SectionRef &Secs : Obj->sections()) {
StringRef Contents = cantFail(Secs.getContents());
StringRef Name = cantFail(Secs.getName());
if (Name == ".debug_abbrev") {
BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents),
Contents.size());
} else if (Name == ".debug_info") {
BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents),
Contents.size());
}
}
// 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());
}
if (DebugNamesTable.isCreated()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_names");
std::unique_ptr<DebugBufferVector> DebugNamesSectionContents =
DebugNamesTable.releaseBuffer();
BC.registerOrUpdateNoteSection(".debug_names",
copyByteArray(*DebugNamesSectionContents),
DebugNamesSectionContents->size());
}
}
void DWARFRewriter::finalizeCompileUnits(DIEBuilder &DIEBlder,
DIEStreamer &Streamer,
CUOffsetMap &CUMap,
const std::list<DWARFUnit *> &CUs,
DebugAddrWriter &FinalAddrWriter) {
for (DWARFUnit *CU : CUs) {
auto AddressWriterIterator = AddressWritersByCU.find(CU->getOffset());
assert(AddressWriterIterator != AddressWritersByCU.end() &&
"AddressWriter does not exist for CU");
DebugAddrWriter *AddressWriter = AddressWriterIterator->second.get();
const size_t BufferOffset = FinalAddrWriter.getBufferSize();
std::optional<uint64_t> Offset = AddressWriter->finalize(BufferOffset);
/// If Offset already exists in UnmodifiedAddressOffsets, then update with
/// Offset, else update with BufferOffset.
if (Offset)
AddressWriter->updateAddrBase(DIEBlder, *CU, *Offset);
else if (AddressWriter->isInitialized())
AddressWriter->updateAddrBase(DIEBlder, *CU, BufferOffset);
if (AddressWriter->isInitialized()) {
std::unique_ptr<AddressSectionBuffer> AddressSectionContents =
AddressWriter->releaseBuffer();
FinalAddrWriter.appendToAddressBuffer(*AddressSectionContents);
}
if (CU->getVersion() != 4)
continue;
std::optional<uint64_t> DWOId = CU->getDWOId();
if (!DWOId)
continue;
auto RangesWriterIterator = LegacyRangesWritersByCU.find(*DWOId);
assert(RangesWriterIterator != LegacyRangesWritersByCU.end() &&
"RangesWriter does not exist for DWOId");
std::unique_ptr<DebugRangesSectionWriter> &LegacyRangesWriter =
RangesWriterIterator->second;
DIE *Die = LegacyRangesWriter->getDie();
if (!Die)
continue;
DIEValue DvalGNUBase = Die->findAttribute(dwarf::DW_AT_GNU_ranges_base);
assert(DvalGNUBase && "GNU_ranges_base attribute does not exist for DWOId");
DIEBlder.replaceValue(
Die, dwarf::DW_AT_GNU_ranges_base, DvalGNUBase.getForm(),
DIEInteger(LegacyRangesSectionWriter->getSectionOffset()));
std::unique_ptr<DebugBufferVector> RangesWritersContents =
LegacyRangesWriter->releaseBuffer();
LegacyRangesSectionWriter->appendToRangeBuffer(*RangesWritersContents);
}
DIEBlder.generateAbbrevs();
DIEBlder.finish();
DIEBlder.updateDebugNamesTable();
// generate debug_info and CUMap
for (DWARFUnit *CU : CUs) {
emitUnit(DIEBlder, Streamer, *CU);
const uint32_t StartOffset = CUOffset;
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*CU);
CUOffset += CU->getHeaderSize();
CUOffset += UnitDIE->getSize();
CUMap[CU->getOffset()] = {StartOffset, CUOffset - StartOffset - 4};
}
}
// 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.
namespace {
std::unique_ptr<BinaryContext>
createDwarfOnlyBC(const object::ObjectFile &File) {
return cantFail(BinaryContext::createBinaryContext(
File.makeTriple(), std::make_shared<orc::SymbolStringPool>(),
File.getFileName(), nullptr, false,
DWARFContext::create(File, DWARFContext::ProcessDebugRelocations::Ignore,
nullptr, "", WithColor::defaultErrorHandler,
WithColor::defaultWarningHandler),
{llvm::outs(), llvm::errs()}));
}
StringMap<DWARFRewriter::KnownSectionsEntry>
createKnownSectionsMap(const MCObjectFileInfo &MCOFI) {
StringMap<DWARFRewriter::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}},
{"debug_loclists.dwo",
{MCOFI.getDwarfLoclistsDWOSection(), DW_SECT_LOCLISTS}},
{"debug_rnglists.dwo",
{MCOFI.getDwarfRnglistsDWOSection(), DW_SECT_RNGLISTS}}};
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.
std::optional<StringRef> updateDebugData(
DWARFContext &DWCtx, StringRef SectionName, StringRef SectionContents,
const StringMap<DWARFRewriter::KnownSectionsEntry> &KnownSections,
MCStreamer &Streamer, DWARFRewriter &Writer,
const DWARFUnitIndex::Entry *CUDWOEntry, uint64_t DWOId,
std::unique_ptr<DebugBufferVector> &OutputBuffer,
DebugRangeListsSectionWriter *RangeListsWriter, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter,
const llvm::bolt::DWARFRewriter::OverriddenSectionsMap &OverridenSections) {
using DWOSectionContribution =
const DWARFUnitIndex::Entry::SectionContribution;
auto getSliceData = [&](const DWARFUnitIndex::Entry *DWOEntry,
StringRef OutData, DWARFSectionKind Sec,
uint64_t &DWPOffset) -> StringRef {
if (DWOEntry) {
DWOSectionContribution *DWOContrubution = DWOEntry->getContribution(Sec);
DWPOffset = DWOContrubution->getOffset();
OutData = OutData.substr(DWPOffset, DWOContrubution->getLength());
}
return OutData;
};
auto SectionIter = KnownSections.find(SectionName);
if (SectionIter == KnownSections.end())
return std::nullopt;
Streamer.switchSection(SectionIter->second.first);
uint64_t DWPOffset = 0;
auto getOverridenSection =
[&](DWARFSectionKind Kind) -> std::optional<StringRef> {
auto Iter = OverridenSections.find(Kind);
if (Iter == OverridenSections.end()) {
errs()
<< "BOLT-WARNING: [internal-dwarf-error]: Could not find overriden "
"section for: "
<< Twine::utohexstr(DWOId) << ".\n";
return std::nullopt;
}
return Iter->second;
};
switch (SectionIter->second.second) {
default: {
if (SectionName != "debug_str.dwo")
errs() << "BOLT-WARNING: unsupported debug section: " << SectionName
<< "\n";
if (StrWriter.isInitialized()) {
OutputBuffer = StrWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
}
return SectionContents;
}
case DWARFSectionKind::DW_SECT_INFO: {
return getOverridenSection(DWARFSectionKind::DW_SECT_INFO);
}
case DWARFSectionKind::DW_SECT_EXT_TYPES: {
return getOverridenSection(DWARFSectionKind::DW_SECT_EXT_TYPES);
}
case DWARFSectionKind::DW_SECT_STR_OFFSETS: {
if (StrOffstsWriter.isFinalized()) {
OutputBuffer = StrOffstsWriter.releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
}
return getSliceData(CUDWOEntry, SectionContents,
DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset);
}
case DWARFSectionKind::DW_SECT_ABBREV: {
return getOverridenSection(DWARFSectionKind::DW_SECT_ABBREV);
}
case DWARFSectionKind::DW_SECT_EXT_LOC:
case DWARFSectionKind::DW_SECT_LOCLISTS: {
OutputBuffer = LocWriter.getBuffer();
// Creating explicit StringRef here, otherwise
// with implicit 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(CUDWOEntry, SectionContents,
DWARFSectionKind::DW_SECT_LINE, DWPOffset);
}
case DWARFSectionKind::DW_SECT_RNGLISTS: {
assert(RangeListsWriter && "RangeListsWriter was not created.");
OutputBuffer = RangeListsWriter->releaseBuffer();
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
OutputBuffer->size());
}
}
}
} // namespace
void DWARFRewriter::writeDWOFiles(
DWARFUnit &CU, const OverriddenSectionsMap &OverridenSections,
const std::string &DWOName, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter, DebugStrWriter &StrWriter,
DebugRangesSectionWriter &TempRangesSectionWriter) {
// Setup DWP code once.
DWARFContext *DWOCtx = BC.getDWOContext();
const uint64_t DWOId = *CU.getDWOId();
const DWARFUnitIndex *CUIndex = nullptr;
bool IsDWP = false;
if (DWOCtx) {
CUIndex = &DWOCtx->getCUIndex();
IsDWP = !CUIndex->getRows().empty();
}
// Skipping CUs that we failed to load.
std::optional<DWARFUnit *> DWOCU = BC.getDWOCU(DWOId);
if (!DWOCU) {
errs() << "BOLT-WARNING: [internal-dwarf-error]: CU for DWO_ID "
<< Twine::utohexstr(DWOId) << " is not found.\n";
return;
}
std::string CompDir = CU.getCompilationDir();
if (!opts::DwarfOutputPath.empty())
CompDir = opts::DwarfOutputPath.c_str();
else if (!opts::CompDirOverride.empty())
CompDir = opts::CompDirOverride;
SmallString<16> AbsolutePath;
sys::path::append(AbsolutePath, CompDir);
sys::path::append(AbsolutePath, DWOName);
std::error_code EC;
std::unique_ptr<ToolOutputFile> TempOut =
std::make_unique<ToolOutputFile>(AbsolutePath, EC, sys::fs::OF_None);
const DWARFUnitIndex::Entry *CUDWOEntry = nullptr;
if (IsDWP)
CUDWOEntry = 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());
const MCObjectFileInfo &MCOFI = *Streamer->getContext().getObjectFileInfo();
StringMap<KnownSectionsEntry> KnownSections = createKnownSectionsMap(MCOFI);
DebugRangeListsSectionWriter *RangeListssWriter = nullptr;
if (CU.getVersion() == 5) {
RangeListssWriter =
llvm::dyn_cast<DebugRangeListsSectionWriter>(&TempRangesSectionWriter);
// Handling .debug_rnglists.dwo separately. The original .o/.dwo might not
// have .debug_rnglists so won't be part of the loop below.
if (!RangeListssWriter->empty()) {
std::unique_ptr<DebugBufferVector> OutputData;
if (std::optional<StringRef> OutData =
updateDebugData((*DWOCU)->getContext(), "debug_rnglists.dwo", "",
KnownSections, *Streamer, *this, CUDWOEntry,
DWOId, OutputData, RangeListssWriter, LocWriter,
StrOffstsWriter, StrWriter, OverridenSections))
Streamer->emitBytes(*OutData);
}
}
for (const SectionRef &Section : File->sections()) {
std::unique_ptr<DebugBufferVector> OutputData;
StringRef SectionName = getSectionName(Section);
if (SectionName == "debug_rnglists.dwo")
continue;
Expected<StringRef> ContentsExp = Section.getContents();
assert(ContentsExp && "Invalid contents.");
if (std::optional<StringRef> OutData = updateDebugData(
(*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections,
*Streamer, *this, CUDWOEntry, DWOId, OutputData, RangeListssWriter,
LocWriter, StrOffstsWriter, StrWriter, OverridenSections))
Streamer->emitBytes(*OutData);
}
Streamer->finish();
TempOut->keep();
}
std::unique_ptr<DebugBufferVector>
DWARFRewriter::makeFinalLocListsSection(DWARFVersion Version) {
auto LocBuffer = std::make_unique<DebugBufferVector>();
auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
auto Writer =
std::unique_ptr<MCObjectWriter>(BC.createObjectWriter(*LocStream));
for (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc :
LocListWritersByCU) {
DebugLocWriter *LocWriter = Loc.second.get();
auto *LocListWriter = llvm::dyn_cast<DebugLoclistWriter>(LocWriter);
// Filter out DWARF4, writing out DWARF5
if (Version == DWARFVersion::DWARF5 &&
(!LocListWriter || LocListWriter->getDwarfVersion() <= 4))
continue;
// Filter out DWARF5, writing out DWARF4
if (Version == DWARFVersion::DWARFLegacy &&
(LocListWriter && LocListWriter->getDwarfVersion() >= 5))
continue;
// Skipping DWARF4/5 split dwarf.
if (LocListWriter && LocListWriter->getDwarfVersion() <= 4)
continue;
std::unique_ptr<DebugBufferVector> CurrCULocationLists =
LocWriter->getBuffer();
*LocStream << *CurrCULocationLists;
}
return LocBuffer;
}
void DWARFRewriter::convertToRangesPatchDebugInfo(
DWARFUnit &Unit, DIEBuilder &DIEBldr, DIE &Die,
uint64_t RangesSectionOffset, DIEValue &LowPCAttrInfo,
DIEValue &HighPCAttrInfo, std::optional<uint64_t> RangesBase) {
dwarf::Form LowForm = LowPCAttrInfo.getForm();
dwarf::Attribute RangeBaseAttribute = dwarf::DW_AT_GNU_ranges_base;
dwarf::Form RangesForm = dwarf::DW_FORM_sec_offset;
if (Unit.getVersion() >= 5) {
RangeBaseAttribute = dwarf::DW_AT_rnglists_base;
RangesForm = dwarf::DW_FORM_rnglistx;
} else if (Unit.getVersion() < 4) {
RangesForm = dwarf::DW_FORM_data4;
}
bool IsUnitDie = Die.getTag() == dwarf::DW_TAG_compile_unit ||
Die.getTag() == dwarf::DW_TAG_skeleton_unit;
if (!IsUnitDie)
DIEBldr.deleteValue(&Die, LowPCAttrInfo.getAttribute());
// In DWARF 5 we can have DW_AT_low_pc either as DW_FORM_addr, or
// DW_FORM_addrx. Former is when DW_AT_rnglists_base is present. Latter is
// when it's absent.
if (IsUnitDie) {
if (LowForm == dwarf::DW_FORM_addrx) {
auto AddrWriterIterator = AddressWritersByCU.find(Unit.getOffset());
assert(AddrWriterIterator != AddressWritersByCU.end() &&
"AddressWriter does not exist for CU");
DebugAddrWriter *AddrWriter = AddrWriterIterator->second.get();
const uint32_t Index = AddrWriter->getIndexFromAddress(0, Unit);
DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
LowPCAttrInfo.getForm(), DIEInteger(Index));
} else {
DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
LowPCAttrInfo.getForm(), DIEInteger(0));
}
// Original CU didn't have DW_AT_*_base. We converted it's children (or
// dwo), so need to insert it into CU.
if (RangesBase) {
if (Unit.getVersion() >= 5) {
DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset,
DIEInteger(*RangesBase));
} else {
DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset,
DIEInteger(INT_MAX));
auto RangesWriterIterator =
LegacyRangesWritersByCU.find(*Unit.getDWOId());
assert(RangesWriterIterator != LegacyRangesWritersByCU.end() &&
"RangesWriter does not exist for DWOId");
RangesWriterIterator->second->setDie(&Die);
}
}
}
// HighPC was conveted into DW_AT_ranges.
// For DWARF5 we only access ranges through index.
DIEBldr.replaceValue(&Die, HighPCAttrInfo.getAttribute(), dwarf::DW_AT_ranges,
RangesForm, DIEInteger(RangesSectionOffset));
}