Summary: * Some cleanup and minor fixes for the new debug information re-writer before moving on to productatization. * The new rewriter wasn't handling binary with DWARF5 and DWARF4 with -fdebug-types-sections. * Removed dead cross cu reference code. * Added support for DW_AT_sibling. * With the new re-writer abbrev number can change which can lead to offset of Type Units changing. Before we would just copy raw data. Changed to write out Type Unit List. This is generated by gdb-add-index. * Fixed how bolt handles gdb-index generated by gdb-11 with types sections. Simplified logic that handles variations of gdb-index. * Clang can generate two type units with the same hash, but different content. LLD does not de-duplicate when ThinLTO is involved. Changed so that TU hash and offset are used to make TU's unique. * It is possible to have references within location expression to another DIE. Fixed it so that relative offset is updated correctly. * Removed all the code related to patching. * Removed dead code. Changed how we handling writting out TUs and TU Index. It now should fully work for DWARF4 and DWARF5. * Removed unused arguments from some APIs, changed return type to void, and other small cleanups. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D46168257
2070 lines
82 KiB
C++
2070 lines
82 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/DIEBuilder.h"
|
|
#include "bolt/Core/DebugData.h"
|
|
#include "bolt/Core/DynoStats.h"
|
|
#include "bolt/Core/ParallelUtilities.h"
|
|
#include "bolt/Rewrite/RewriteInstance.h"
|
|
#include "bolt/Utils/Utils.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/DWARFStreamer.h"
|
|
#include "llvm/DWP/DWP.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.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/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/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/ThreadPool.h"
|
|
#include "llvm/Support/ToolOutputFile.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"
|
|
|
|
LLVM_ATTRIBUTE_UNUSED
|
|
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);
|
|
}
|
|
|
|
namespace llvm {
|
|
namespace bolt {
|
|
/// Emits debug information into .debug_info or .debug_types section.
|
|
class DIEStreamer : public DwarfStreamer {
|
|
DIEBuilder *DIEBldr;
|
|
DWARFRewriter &Rewriter;
|
|
|
|
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);
|
|
Rewriter.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, DWARFRewriter &Rewriter,
|
|
DWARFLinker::OutputFileType OutFileType,
|
|
raw_pwrite_stream &OutFile,
|
|
std::function<StringRef(StringRef Input)> Translator,
|
|
DWARFLinker::messageHandler Warning)
|
|
: DwarfStreamer(OutFileType, OutFile, Translator, Warning),
|
|
DIEBldr(DIEBldr), Rewriter(Rewriter){};
|
|
|
|
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<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
|
|
|
|
static bool getLowAndHighPC(const DIE &Die, const DWARFUnit &DU,
|
|
uint64_t &LowPC, uint64_t &HighPC,
|
|
uint64_t &SectionIndex) {
|
|
DIEValue DvalLowPc = Die.findAttribute(dwarf::DW_AT_low_pc);
|
|
DIEValue DvalHighPc = Die.findAttribute(dwarf::DW_AT_high_pc);
|
|
if (!DvalLowPc || !DvalHighPc)
|
|
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;
|
|
}
|
|
if (DvalHighPc.getForm() == dwarf::DW_FORM_addr)
|
|
HighPC = DvalHighPc.getDIEInteger().getValue();
|
|
else
|
|
HighPC = LowPC + DvalHighPc.getDIEInteger().getValue();
|
|
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// 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) {
|
|
std::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 std::unique_ptr<DIEStreamer>
|
|
createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
|
|
StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr,
|
|
DWARFRewriter &Rewriter) {
|
|
|
|
std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
|
|
&DIEBldr, Rewriter, llvm::DWARFLinker::OutputFileType::Object, OutFile,
|
|
[](StringRef Input) -> StringRef { return Input; },
|
|
[&](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 DWARFRewriter::UnitMeta emitUnit(DIEBuilder &DIEBldr,
|
|
std::unique_ptr<DIEStreamer> &Streamer,
|
|
DWARFUnit &Unit) {
|
|
DIE *UnitDIE = DIEBldr.getUnitDIEbyUnit(Unit);
|
|
const DIEBuilder::DWARFUnitInfo &U = DIEBldr.getUnitInfoByDwarfUnit(Unit);
|
|
Streamer->emitUnit(Unit, *UnitDIE);
|
|
uint64_t TypeHash = 0;
|
|
if (DWARFTypeUnit *DTU = dyn_cast_or_null<DWARFTypeUnit>(&Unit))
|
|
TypeHash = DTU->getTypeHash();
|
|
return {U.UnitOffset, U.UnitLength, TypeHash};
|
|
}
|
|
|
|
static void emitDWOBuilder(DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter,
|
|
const DWARFUnit &SplitCU, const uint64_t DWOId) {
|
|
// 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, Rewriter);
|
|
if (SplitCU.getContext().getMaxDWOVersion() >= 5) {
|
|
// TODO: Handle DWP as input. Right now it will iterate over all of CUs and
|
|
// TUs
|
|
for (std::unique_ptr<llvm::DWARFUnit> &CU :
|
|
SplitCU.getContext().dwo_info_section_units()) {
|
|
DWARFRewriter::UnitMeta MI = emitUnit(DWODIEBuilder, Streamer, *CU.get());
|
|
if (CU->isTypeUnit())
|
|
Rewriter.addTUnitMetaEntry(DWOId, MI);
|
|
else
|
|
Rewriter.addCUnitMetaEntry(DWOId, MI);
|
|
}
|
|
} 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()) {
|
|
DWARFRewriter::UnitMeta MI = emitUnit(DWODIEBuilder, Streamer, *CU);
|
|
Rewriter.addTUnitMetaEntry(DWOId, MI);
|
|
}
|
|
}
|
|
|
|
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");
|
|
|
|
for (const SectionRef &Secs : Obj->sections()) {
|
|
StringRef Contents = cantFail(Secs.getContents());
|
|
StringRef Name = cantFail(Secs.getName());
|
|
if (Name.equals(".debug_abbrev"))
|
|
Rewriter.setDwoDebugAbbrevStr(DWOId, Contents);
|
|
else if (Name.equals(".debug_info"))
|
|
Rewriter.setDwoDebugInfoStr(DWOId, Contents);
|
|
else if (Name.equals(".debug_types"))
|
|
Rewriter.setDwoDebugTypeStr(DWOId, Contents);
|
|
}
|
|
}
|
|
|
|
void DWARFRewriter::addStringHelper(DIEBuilder &DIEBldr, DIE &Die,
|
|
const DWARFUnit &Unit,
|
|
DIEValue &DIEAttrInfo, StringRef Str) {
|
|
uint32_t NewOffset = StrWriter->addString(Str);
|
|
if (Unit.getVersion() == 5) {
|
|
StrOffstsWriter->updateAddressMap(DIEAttrInfo.getDIEInteger().getValue(),
|
|
NewOffset);
|
|
return;
|
|
}
|
|
DIEBldr.replaceValue(&Die, DIEAttrInfo.getAttribute(), DIEAttrInfo.getForm(),
|
|
DIEInteger(NewOffset));
|
|
}
|
|
|
|
void DWARFRewriter::updateDebugInfo() {
|
|
ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
|
|
if (!DebugInfo)
|
|
return;
|
|
|
|
ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
|
|
StrWriter = std::make_unique<DebugStrWriter>(BC);
|
|
|
|
StrOffstsWriter = std::make_unique<DebugStrOffsetsWriter>();
|
|
|
|
if (!opts::DeterministicDebugInfo) {
|
|
opts::DeterministicDebugInfo = true;
|
|
errs() << "BOLT-WARNING: --deterministic-debuginfo is being deprecated\n";
|
|
}
|
|
|
|
if (BC.isDWARF5Used()) {
|
|
AddrWriter = std::make_unique<DebugAddrWriterDwarf5>(&BC);
|
|
RangeListsSectionWriter = std::make_unique<DebugRangeListsSectionWriter>();
|
|
DebugRangeListsSectionWriter::setAddressWriter(AddrWriter.get());
|
|
} else {
|
|
AddrWriter = std::make_unique<DebugAddrWriter>(&BC);
|
|
}
|
|
|
|
if (BC.isDWARFLegacyUsed())
|
|
LegacyRangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
|
|
|
|
DebugLoclistWriter::setAddressWriter(AddrWriter.get());
|
|
|
|
size_t CUIndex = 0;
|
|
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
const uint16_t DwarfVersion = CU->getVersion();
|
|
if (DwarfVersion >= 5) {
|
|
LocListWritersByCU[CUIndex] =
|
|
std::make_unique<DebugLoclistWriter>(*CU.get(), DwarfVersion, false);
|
|
|
|
if (std::optional<uint64_t> DWOId = CU->getDWOId()) {
|
|
assert(RangeListsWritersByCU.count(*DWOId) == 0 &&
|
|
"RangeLists writer for DWO unit already exists.");
|
|
auto RangeListsSectionWriter =
|
|
std::make_unique<DebugRangeListsSectionWriter>();
|
|
RangeListsSectionWriter->initSection(*CU.get());
|
|
RangeListsWritersByCU[*DWOId] = std::move(RangeListsSectionWriter);
|
|
}
|
|
|
|
} else {
|
|
LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>();
|
|
}
|
|
|
|
if (std::optional<uint64_t> DWOId = CU->getDWOId()) {
|
|
assert(LocListWritersByCU.count(*DWOId) == 0 &&
|
|
"LocList writer for DWO unit already exists.");
|
|
// Work around some bug in llvm-15. If I pass in directly lld reports
|
|
// undefined symbol.
|
|
LocListWritersByCU[*DWOId] =
|
|
std::make_unique<DebugLoclistWriter>(*CU.get(), DwarfVersion, true);
|
|
}
|
|
++CUIndex;
|
|
}
|
|
|
|
// 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, DIEBuilder &DIEBldr,
|
|
DIE &UnitDIE) -> void {
|
|
DIEValue DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_dwo_name);
|
|
if (!DWONameAttrInfo)
|
|
DWONameAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_GNU_dwo_name);
|
|
assert(DWONameAttrInfo && "DW_AT_dwo_name is not in Skeleton CU.");
|
|
std::string ObjectName;
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
ObjectName = getDWOName(Unit, &NameToIndexMap, DWOIdToName);
|
|
}
|
|
addStringHelper(DIEBldr, UnitDIE, Unit, DWONameAttrInfo,
|
|
ObjectName.c_str());
|
|
|
|
DIEValue CompDirAttrInfo = UnitDIE.findAttribute(dwarf::DW_AT_comp_dir);
|
|
assert(CompDirAttrInfo && "DW_AT_comp_dir is not in Skeleton CU.");
|
|
|
|
if (!opts::DwarfOutputPath.empty()) {
|
|
addStringHelper(DIEBldr, UnitDIE, Unit, CompDirAttrInfo,
|
|
opts::DwarfOutputPath.c_str());
|
|
}
|
|
};
|
|
|
|
auto processUnitDIE = [&](size_t CUIndex, DWARFUnit *Unit,
|
|
DIEBuilder *DIEBlder) {
|
|
// Check if the unit is a skeleton and we need special updates for it and
|
|
// its matching split/DWO CU.
|
|
std::optional<DWARFUnit *> SplitCU;
|
|
std::optional<uint64_t> RangesBase;
|
|
std::optional<uint64_t> DWOId = Unit->getDWOId();
|
|
StrOffstsWriter->initialize(Unit->getStringOffsetSection(),
|
|
Unit->getStringOffsetsTableContribution());
|
|
if (DWOId)
|
|
SplitCU = BC.getDWOCU(*DWOId);
|
|
|
|
DebugLocWriter *DebugLocWriter = nullptr;
|
|
DebugRangesSectionWriter *RangesSectionWriter =
|
|
Unit->getVersion() >= 5 ? RangeListsSectionWriter.get()
|
|
: LegacyRangesSectionWriter.get();
|
|
// Skipping CUs that failed to load.
|
|
if (SplitCU) {
|
|
DIEBuilder DWODIEBuilder(&(*SplitCU)->getContext(), true);
|
|
updateDWONameCompDir(*Unit, *DIEBlder,
|
|
*DIEBlder->getUnitDIEbyUnit(*Unit));
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
DebugLocWriter = LocListWritersByCU[*DWOId].get();
|
|
}
|
|
DebugRangesSectionWriter *TempRangesSectionWriter = RangesSectionWriter;
|
|
if (Unit->getVersion() >= 5) {
|
|
TempRangesSectionWriter = RangeListsWritersByCU[*DWOId].get();
|
|
} else {
|
|
RangesBase = RangesSectionWriter->getSectionOffset();
|
|
// For DWARF5 there is now .debug_rnglists.dwo, so don't need to
|
|
// update rnglists base.
|
|
if (RangesBase) {
|
|
DwoRangesBase[*DWOId] = *RangesBase;
|
|
setDwoRangesBase(*DWOId, *RangesBase);
|
|
}
|
|
}
|
|
|
|
updateUnitDebugInfo(*(*SplitCU), DWODIEBuilder, *DebugLocWriter,
|
|
*TempRangesSectionWriter);
|
|
DebugLocWriter->finalize(DWODIEBuilder,
|
|
*DWODIEBuilder.getUnitDIEbyUnit(**SplitCU));
|
|
if (Unit->getVersion() >= 5)
|
|
TempRangesSectionWriter->finalizeSection();
|
|
|
|
emitDWOBuilder(DWODIEBuilder, *this, **SplitCU, *DWOId);
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(AccessMutex);
|
|
auto LocListWriterIter = LocListWritersByCU.find(CUIndex);
|
|
if (LocListWriterIter != LocListWritersByCU.end())
|
|
DebugLocWriter = LocListWriterIter->second.get();
|
|
}
|
|
if (Unit->getVersion() >= 5) {
|
|
RangesBase = RangesSectionWriter->getSectionOffset() +
|
|
getDWARF5RngListLocListHeaderSize();
|
|
RangesSectionWriter->initSection(*Unit);
|
|
StrOffstsWriter->finalizeSection(*Unit);
|
|
}
|
|
|
|
updateUnitDebugInfo(*Unit, *DIEBlder, *DebugLocWriter, *RangesSectionWriter,
|
|
RangesBase);
|
|
DebugLocWriter->finalize(*DIEBlder, *DIEBlder->getUnitDIEbyUnit(*Unit));
|
|
if (Unit->getVersion() >= 5)
|
|
RangesSectionWriter->finalizeSection();
|
|
};
|
|
|
|
CUIndex = 0;
|
|
DIEBuilder DIEBlder(BC.DwCtx.get());
|
|
if (opts::NoThreads || opts::DeterministicDebugInfo) {
|
|
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
processUnitDIE(CUIndex++, CU.get(), &DIEBlder);
|
|
}
|
|
} else {
|
|
// Update unit debug info in parallel
|
|
ThreadPool &ThreadPool = ParallelUtilities::getThreadPool();
|
|
for (std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
ThreadPool.async(processUnitDIE, CUIndex, CU.get(), &DIEBlder);
|
|
CUIndex++;
|
|
}
|
|
ThreadPool.wait();
|
|
}
|
|
|
|
CUOffsetMap OffsetMap = finalizeDebugSections(DIEBlder);
|
|
|
|
if (opts::WriteDWP)
|
|
writeDWP(DWOIdToName);
|
|
else
|
|
writeDWOFiles(DWOIdToName);
|
|
|
|
updateGdbIndexSection(OffsetMap, CUIndex);
|
|
}
|
|
|
|
void DWARFRewriter::updateUnitDebugInfo(
|
|
DWARFUnit &Unit, DIEBuilder &DIEBldr, DebugLocWriter &DebugLocWriter,
|
|
DebugRangesSectionWriter &RangesSectionWriter,
|
|
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);
|
|
|
|
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);
|
|
if (!Unit.isDWOUnit())
|
|
ARangesSectionWriter->addCURanges(Unit.getOffset(),
|
|
std::move(OutputRanges));
|
|
updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset,
|
|
0, 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_t SectionIndex, HighPC;
|
|
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;
|
|
|
|
Address = Ranges.front().LowPC;
|
|
}
|
|
|
|
// 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();
|
|
|
|
if (FunctionRanges.empty())
|
|
FunctionRanges.push_back({0, 0});
|
|
|
|
updateDWARFObjectAddressRanges(
|
|
Unit, DIEBldr, *Die, RangesSectionWriter.addRanges(FunctionRanges),
|
|
0);
|
|
|
|
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 =
|
|
getDIEAddressRanges(*Die, Unit);
|
|
const BinaryFunction *Function =
|
|
RangesOrError && !RangesOrError->empty()
|
|
? BC.getBinaryFunctionContainingAddress(
|
|
RangesOrError->front().LowPC)
|
|
: nullptr;
|
|
bool ErrorState = false;
|
|
std::optional<uint64_t> NewLowPC;
|
|
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';
|
|
});
|
|
if (!OutputRanges.empty())
|
|
NewLowPC = OutputRanges.front().LowPC;
|
|
RangesSectionOffset = RangesSectionWriter.addRanges(
|
|
std::move(OutputRanges), CachedRanges);
|
|
} else if (!RangesOrError) {
|
|
ErrorState = true;
|
|
consumeError(RangesOrError.takeError());
|
|
}
|
|
|
|
uint64_t LowPCToUse = 0;
|
|
if (!ErrorState && RangesOrError.get().size() == 1 &&
|
|
RangesOrError.get().begin()->LowPC ==
|
|
RangesOrError.get().begin()->HighPC) {
|
|
if (NewLowPC)
|
|
LowPCToUse = NewLowPC.value();
|
|
else
|
|
LowPCToUse = RangesOrError.get().begin()->LowPC;
|
|
}
|
|
|
|
updateDWARFObjectAddressRanges(Unit, DIEBldr, *Die, RangesSectionOffset,
|
|
LowPCToUse);
|
|
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 =
|
|
AddrWriter->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 = Function->translateInputToOutputLocationList(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);
|
|
}
|
|
PrevOffset = CurEndOffset;
|
|
continue;
|
|
}
|
|
|
|
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.");
|
|
if (Expr.getCode() == dwarf::DW_OP_addrx) {
|
|
const uint32_t Index = AddrWriter->getIndexFromAddress(
|
|
EntryAddress->Address, Unit);
|
|
// update Index 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(Index, OSE);
|
|
|
|
DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0),
|
|
dwarf::DW_FORM_data1,
|
|
DIEInteger(dwarf::DW_OP_addrx));
|
|
NewExprSize += 1;
|
|
for (uint8_t Byte : Tmp) {
|
|
DIEBldr.addValue(NewAttr, static_cast<dwarf::Attribute>(0),
|
|
dwarf::DW_FORM_data1, DIEInteger(Byte));
|
|
NewExprSize += 1;
|
|
}
|
|
} else {
|
|
// TODO: Re-do this as DWARF5.
|
|
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);
|
|
}
|
|
AddrWriter->addIndexAddress(EntryAddress->Address,
|
|
static_cast<uint32_t>(Index), Unit);
|
|
}
|
|
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) {
|
|
const std::optional<uint64_t> Result =
|
|
LowPCAttrInfo.getDIEInteger().getValue();
|
|
if (Result.has_value()) {
|
|
const uint64_t Address = Result.value();
|
|
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_GNU_addr_index) {
|
|
// 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.
|
|
// TODO: We can now re-write .debug_info. This can be simplified to
|
|
// just getting a new index and creating a patch.
|
|
const uint64_t Index = *Result;
|
|
AddrWriter->addIndexAddress(NewAddress ? NewAddress : Address,
|
|
Index, Unit);
|
|
} else if (Form == dwarf::DW_FORM_addrx) {
|
|
const uint32_t Index = AddrWriter->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,
|
|
uint64_t LowPCToUse, 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) {
|
|
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;
|
|
|
|
uint64_t CurRangeBase = 0;
|
|
if (std::optional<uint64_t> DWOId = Unit.getDWOId()) {
|
|
CurRangeBase = getDwoRangesBase(*DWOId);
|
|
}
|
|
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 - CurRangeBase));
|
|
|
|
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(LowPCToUse));
|
|
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(*RangesBase));
|
|
else if (Unit.getVersion() == 5)
|
|
DIEBldr.addValue(&Die, dwarf::DW_AT_rnglists_base,
|
|
dwarf::DW_FORM_sec_offset, DIEInteger(*RangesBase));
|
|
else
|
|
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, LowPCToUse,
|
|
RangesBase);
|
|
} else {
|
|
if (opts::Verbosity >= 1)
|
|
errs() << "BOLT-ERROR: cannot update ranges for DIE in Unit offset 0x"
|
|
<< Unit.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.");
|
|
|
|
// 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) {
|
|
std::optional<DWARFFormValue> StmtList =
|
|
Unit->getUnitDIE().find(dwarf::DW_AT_stmt_list);
|
|
std::optional<uint64_t> Offset = dwarf::toSectionOffset(StmtList);
|
|
assert(Offset && "Was not able to retreive value of DW_AT_stmt_list.");
|
|
return *Offset;
|
|
};
|
|
|
|
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;
|
|
|
|
std::optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(CU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list);
|
|
if (!AttrVal)
|
|
continue;
|
|
|
|
const uint64_t LineTableOffset = Layout.getSymbolOffset(*Label);
|
|
DebugLineOffsetMap[GetStatementListValue(CU.get())] = LineTableOffset;
|
|
assert(DbgInfoSection && ".debug_info section must exist");
|
|
LineTablePatchMap[CU.get()] = LineTableOffset;
|
|
}
|
|
|
|
for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units()) {
|
|
DWARFUnit *Unit = TU.get();
|
|
std::optional<AttrInfo> AttrVal =
|
|
findAttributeInfo(TU.get()->getUnitDIE(), dwarf::DW_AT_stmt_list);
|
|
if (!AttrVal)
|
|
continue;
|
|
auto Iter = DebugLineOffsetMap.find(GetStatementListValue(Unit));
|
|
assert(Iter != DebugLineOffsetMap.end() &&
|
|
"Type Unit Updated Line Number Entry does not exist.");
|
|
TypeUnitRelocMap[Unit] = 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::finalizeDebugSections(DIEBuilder &DIEBlder) {
|
|
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());
|
|
}
|
|
|
|
// 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 (std::unique_ptr<llvm::DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
DIE *Die = DIEBlder.getUnitDIEbyUnit(*CU.get());
|
|
uint64_t Offset = 0;
|
|
DIEValue GnuAddrBaseAttrInfo =
|
|
Die->findAttribute(dwarf::DW_AT_GNU_addr_base);
|
|
DIEValue AddrBaseAttrInfo = Die->findAttribute(dwarf::DW_AT_addr_base);
|
|
dwarf::Form BaseAttrForm;
|
|
dwarf::Attribute BaseAttr;
|
|
// For cases where Skeleton CU does not have DW_AT_GNU_addr_base
|
|
if (!GnuAddrBaseAttrInfo && CU->getVersion() < 5)
|
|
continue;
|
|
|
|
if (!AddrBaseAttrInfo && CU->getVersion() >= 5 &&
|
|
!AddrWriter->doesCUExist(*CU))
|
|
continue;
|
|
|
|
Offset = AddrWriter->getOffset(*CU);
|
|
|
|
if (GnuAddrBaseAttrInfo) {
|
|
BaseAttrForm = GnuAddrBaseAttrInfo.getForm();
|
|
BaseAttr = GnuAddrBaseAttrInfo.getAttribute();
|
|
}
|
|
|
|
if (AddrBaseAttrInfo) {
|
|
BaseAttrForm = AddrBaseAttrInfo.getForm();
|
|
BaseAttr = AddrBaseAttrInfo.getAttribute();
|
|
}
|
|
|
|
if (GnuAddrBaseAttrInfo || AddrBaseAttrInfo) {
|
|
DIEBlder.replaceValue(Die, BaseAttr, BaseAttrForm, DIEInteger(Offset));
|
|
} else if (CU->getVersion() >= 5) {
|
|
// A case where we were not using .debug_addr section, but after update
|
|
// now using it.
|
|
DIEBlder.addValue(Die, dwarf::DW_AT_addr_base,
|
|
dwarf::DW_FORM_sec_offset, DIEInteger(Offset));
|
|
}
|
|
}
|
|
}
|
|
|
|
// update TypeUnit DW_AT_stmt_list with new .debug_line information.
|
|
for (const std::unique_ptr<DWARFUnit> &TU : BC.DwCtx->types_section_units()) {
|
|
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(*TU.get());
|
|
DIEValue StmtAttrInfo = UnitDIE->findAttribute(dwarf::DW_AT_stmt_list);
|
|
if (!StmtAttrInfo || !TypeUnitRelocMap.count(TU.get()))
|
|
continue;
|
|
DIEBlder.replaceValue(UnitDIE, dwarf::DW_AT_stmt_list,
|
|
StmtAttrInfo.getForm(),
|
|
DIEInteger(TypeUnitRelocMap[TU.get()]));
|
|
}
|
|
|
|
// generate and populate abbrevs here
|
|
DIEBlder.generateAbbrevs();
|
|
DIEBlder.finish();
|
|
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> Streamer = createDIEStreamer(
|
|
*TheTriple, *ObjOS, "AbbrevStreamerInitAug2", DIEBlder, *this);
|
|
|
|
// generate debug_info and CUMap
|
|
CUOffsetMap CUMap;
|
|
uint32_t CUOffset = 0;
|
|
for (std::unique_ptr<llvm::DWARFUnit> &CU : BC.DwCtx->info_section_units()) {
|
|
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())
|
|
emitUnit(DIEBlder, Streamer, *TU);
|
|
|
|
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.equals(".debug_abbrev")) {
|
|
BC.registerOrUpdateNoteSection(".debug_abbrev", copyByteArray(Contents),
|
|
Contents.size());
|
|
} else if (Name.equals(".debug_info")) {
|
|
BC.registerOrUpdateNoteSection(".debug_info", copyByteArray(Contents),
|
|
Contents.size());
|
|
} else if (Name.equals(".debug_types")) {
|
|
BC.registerOrUpdateNoteSection(".debug_types", 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());
|
|
}
|
|
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 cantFail(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}},
|
|
{"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<KnownSectionsEntry> &KnownSections, MCStreamer &Streamer,
|
|
DWARFRewriter &Writer, const DWARFUnitIndex::Entry *CUDWOEntry,
|
|
uint64_t DWOId, std::unique_ptr<DebugBufferVector> &OutputBuffer,
|
|
DebugRangeListsSectionWriter *RangeListsWriter) {
|
|
|
|
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;
|
|
|
|
switch (SectionIter->second.second) {
|
|
default: {
|
|
if (!SectionName.equals("debug_str.dwo"))
|
|
errs() << "BOLT-WARNING: unsupported debug section: " << SectionName
|
|
<< "\n";
|
|
return SectionContents;
|
|
}
|
|
case DWARFSectionKind::DW_SECT_INFO: {
|
|
return Writer.getDwoDebugInfoStr(DWOId);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_EXT_TYPES: {
|
|
return Writer.getDwoDebugTypeStr(DWOId);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_STR_OFFSETS: {
|
|
return getSliceData(CUDWOEntry, SectionContents,
|
|
DWARFSectionKind::DW_SECT_STR_OFFSETS, DWPOffset);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_ABBREV: {
|
|
return Writer.getDwoDebugAbbrevStr(DWOId);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_EXT_LOC:
|
|
case DWARFSectionKind::DW_SECT_LOCLISTS: {
|
|
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(CUDWOEntry, SectionContents,
|
|
DWARFSectionKind::DW_SECT_LINE, DWPOffset);
|
|
}
|
|
case DWARFSectionKind::DW_SECT_RNGLISTS: {
|
|
OutputBuffer = RangeListsWriter->releaseBuffer();
|
|
return StringRef(reinterpret_cast<const char *>(OutputBuffer->data()),
|
|
OutputBuffer->size());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // 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;
|
|
MapVector<uint64_t, UnitIndexEntry> TypeIndexEntries;
|
|
uint16_t Version = 0;
|
|
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();
|
|
}
|
|
uint64_t DebugInfoSize = 0;
|
|
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
|
|
std::optional<uint64_t> DWOId = CU->getDWOId();
|
|
if (!DWOId)
|
|
continue;
|
|
|
|
// Skipping CUs that we failed to load.
|
|
std::optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId);
|
|
if (!DWOCU)
|
|
continue;
|
|
|
|
if (Version == 0) {
|
|
Version = CU->getVersion();
|
|
IndexVersion = Version < 5 ? 2 : 5;
|
|
} else if (Version != CU->getVersion()) {
|
|
errs() << "BOLT-ERROR: Incompatible DWARF compile unit versions.\n";
|
|
exit(1);
|
|
}
|
|
|
|
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 *CUDWOEntry = nullptr;
|
|
if (IsDWP)
|
|
CUDWOEntry = CUIndex->getFromHash(*DWOId);
|
|
|
|
bool StrSectionWrittenOut = false;
|
|
const object::ObjectFile *DWOFile =
|
|
(*DWOCU)->getContext().getDWARFObj().getFile();
|
|
|
|
DebugRangeListsSectionWriter *RangeListssWriter = nullptr;
|
|
if (CU->getVersion() == 5) {
|
|
assert(RangeListsWritersByCU.count(*DWOId) != 0 &&
|
|
"No RangeListsWriter for DWO ID.");
|
|
RangeListssWriter = RangeListsWritersByCU[*DWOId].get();
|
|
}
|
|
auto AddType = [&](unsigned int Index, uint32_t IndexVersion,
|
|
uint64_t Offset, uint64_t Length,
|
|
uint64_t Hash) -> void {
|
|
UnitIndexEntry TUEntry = CurEntry;
|
|
if (IndexVersion < 5)
|
|
TUEntry.Contributions[0] = {};
|
|
TUEntry.Contributions[Index].setOffset(Offset);
|
|
TUEntry.Contributions[Index].setLength(Length);
|
|
ContributionOffsets[Index] += TUEntry.Contributions[Index].getLength32();
|
|
TypeIndexEntries.insert(std::make_pair(Hash, TUEntry));
|
|
};
|
|
for (const SectionRef &Section : DWOFile->sections()) {
|
|
std::unique_ptr<DebugBufferVector> OutputData;
|
|
StringRef SectionName = getSectionName(Section);
|
|
Expected<StringRef> ContentsExp = Section.getContents();
|
|
assert(ContentsExp && "Invalid contents.");
|
|
std::optional<StringRef> TOutData = updateDebugData(
|
|
(*DWOCU)->getContext(), SectionName, *ContentsExp, KnownSections,
|
|
*Streamer, *this, CUDWOEntry, *DWOId, OutputData, RangeListssWriter);
|
|
if (!TOutData)
|
|
continue;
|
|
|
|
StringRef OutData = *TOutData;
|
|
if (SectionName == "debug_types.dwo") {
|
|
Streamer->emitBytes(OutData);
|
|
continue;
|
|
}
|
|
|
|
if (SectionName.equals("debug_str.dwo")) {
|
|
CurStrSection = OutData;
|
|
} else {
|
|
// Since handleDebugDataPatching returned true, we already know this is
|
|
// a known section.
|
|
auto SectionIter = KnownSections.find(SectionName);
|
|
if (SectionIter->second.second == DWARFSectionKind::DW_SECT_STR_OFFSETS)
|
|
CurStrOffsetSection = OutData;
|
|
else
|
|
Streamer->emitBytes(OutData);
|
|
unsigned int Index =
|
|
getContributionIndex(SectionIter->second.second, IndexVersion);
|
|
uint64_t Offset = ContributionOffsets[Index];
|
|
uint64_t Length = OutData.size();
|
|
if (CU->getVersion() >= 5 &&
|
|
SectionIter->second.second == DWARFSectionKind::DW_SECT_INFO) {
|
|
for (UnitMeta &MI : getTUnitMetaEntries(*DWOId))
|
|
MI.Offset += DebugInfoSize;
|
|
|
|
const UnitMeta &MI = getCUnitMetaEntry(*DWOId);
|
|
Offset = DebugInfoSize + MI.Offset;
|
|
Length = MI.Length;
|
|
DebugInfoSize += OutData.size();
|
|
}
|
|
CurEntry.Contributions[Index].setOffset(Offset);
|
|
CurEntry.Contributions[Index].setLength(Length);
|
|
ContributionOffsets[Index] +=
|
|
CurEntry.Contributions[Index].getLength32();
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Handling TU
|
|
const unsigned Index = getContributionIndex(
|
|
IndexVersion < 5 ? DW_SECT_EXT_TYPES : DW_SECT_INFO, IndexVersion);
|
|
for (UnitMeta &MI : getTUnitMetaEntries(*DWOId))
|
|
AddType(Index, IndexVersion, MI.Offset, MI.Length, MI.TUHash);
|
|
}
|
|
|
|
if (Version < 5) {
|
|
// Lie about there being no info contributions so the TU index only includes
|
|
// the type unit contribution for DWARF < 5. In DWARFv5 the TU index has a
|
|
// contribution to the info section, so we do not want to lie about it.
|
|
ContributionOffsets[0] = 0;
|
|
}
|
|
writeIndex(*Streamer.get(), MCOFI.getDwarfTUIndexSection(),
|
|
ContributionOffsets, TypeIndexEntries, IndexVersion);
|
|
|
|
if (Version < 5) {
|
|
// 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;
|
|
// Unlie about the info contribution
|
|
ContributionOffsets[0] = 1;
|
|
}
|
|
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()) {
|
|
std::optional<uint64_t> DWOId = CU->getDWOId();
|
|
if (!DWOId)
|
|
continue;
|
|
|
|
// Skipping CUs that we failed to load.
|
|
std::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 *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) {
|
|
assert(RangeListsWritersByCU.count(*DWOId) != 0 &&
|
|
"No RangeListsWriter for DWO ID.");
|
|
RangeListssWriter = RangeListsWritersByCU[*DWOId].get();
|
|
|
|
// Handling .debug_rnglists.dwo seperatly. 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))
|
|
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))
|
|
Streamer->emitBytes(*OutData);
|
|
}
|
|
Streamer->finish();
|
|
TempOut->keep();
|
|
}
|
|
}
|
|
|
|
void DWARFRewriter::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) {
|
|
std::lock_guard<std::mutex> Lock(DWARFRewriterMutex);
|
|
if (!BC.getGdbIndexSection())
|
|
return;
|
|
GDBIndexTUEntryVector.emplace_back(Entry);
|
|
}
|
|
|
|
void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap, uint32_t NumCUs) {
|
|
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 uint32_t TUListSize = AddressTableOffset - CUTypesOffset;
|
|
const unsigned NUmCUsEncoded = CUListSize / 16;
|
|
unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion();
|
|
unsigned NumDWARF5TUs =
|
|
getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits();
|
|
bool SkipTypeUnits = false;
|
|
// For DWARF5 Types are in .debug_info.
|
|
// LLD doesn't generate Types CU List, and in CU list offset
|
|
// only includes CUs.
|
|
// GDB 11+ includes only CUs in CU list and generates Types
|
|
// list.
|
|
// GDB 9 includes CUs and TUs in CU list and generates TYpes
|
|
// list. The NumCUs is CUs + TUs, so need to modify the check.
|
|
// For split-dwarf
|
|
// GDB-11, DWARF5: TU units from dwo are not included.
|
|
// GDB-11, DWARF4: TU units from dwo are included.
|
|
if (MaxDWARFVersion >= 5)
|
|
SkipTypeUnits = !TUListSize ? true
|
|
: ((NUmCUsEncoded + NumDWARF5TUs) ==
|
|
BC.DwCtx->getNumCompileUnits());
|
|
|
|
if (!((CUListSize == NumCUs * 16) ||
|
|
(CUListSize == (NumCUs + NumDWARF5TUs) * 16))) {
|
|
errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n";
|
|
exit(1);
|
|
}
|
|
DenseSet<uint64_t> OriginalOffsets;
|
|
for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits();
|
|
Index < Units; ++Index) {
|
|
const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index);
|
|
if (SkipTypeUnits && CU->isTypeUnit())
|
|
continue;
|
|
const uint64_t Offset = read64le(Data);
|
|
Data += 16;
|
|
if (CU->getOffset() != Offset) {
|
|
errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n";
|
|
exit(1);
|
|
}
|
|
|
|
OriginalOffsets.insert(Offset);
|
|
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) {
|
|
// Skipping TU for DWARF5 when they are not included in CU list.
|
|
if (!OriginalOffsets.count(CUInfo.first))
|
|
continue;
|
|
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;
|
|
}
|
|
|
|
// Rewrite TU CU List, since abbrevs can be different.
|
|
// Entry example:
|
|
// 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature =
|
|
// 0x418503b8111e9a7b 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.
|
|
if (TUListSize)
|
|
for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) {
|
|
write64le(Buffer, Entry.UnitOffset);
|
|
write64le(Buffer + 8, Entry.TypeDIERelativeOffset);
|
|
write64le(Buffer + 16, Entry.TypeHash);
|
|
Buffer += sizeof(GDBIndexTUEntry);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
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 ||
|
|
(LocListWriter->getDwarfVersion() >= 5 &&
|
|
LocListWriter->isSplitDwarf()))) {
|
|
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, uint64_t LowPCToUse,
|
|
std::optional<uint64_t> RangesBase) {
|
|
uint32_t BaseOffset = 0;
|
|
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;
|
|
}
|
|
|
|
// In DWARF4 for DW_AT_low_pc in binary DW_FORM_addr is used. In the DWO
|
|
// section DW_FORM_GNU_addr_index is used. So for if we are converting
|
|
// DW_AT_low_pc/DW_AT_high_pc and see DW_FORM_GNU_addr_index. We are
|
|
// converting in DWO section, and DW_AT_ranges [DW_FORM_sec_offset] is
|
|
// relative to DW_AT_GNU_ranges_base.
|
|
if (LowForm == dwarf::DW_FORM_GNU_addr_index) {
|
|
// Use ULEB128 for the value.
|
|
DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
|
|
LowPCAttrInfo.getForm(), DIEInteger(0));
|
|
// Ranges are relative to DW_AT_GNU_ranges_base.
|
|
uint64_t CurRangeBase = 0;
|
|
if (std::optional<uint64_t> DWOId = Unit.getDWOId()) {
|
|
CurRangeBase = getDwoRangesBase(*DWOId);
|
|
}
|
|
BaseOffset = CurRangeBase;
|
|
} else {
|
|
// 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 (LowForm == dwarf::DW_FORM_addrx) {
|
|
const uint32_t Index = AddrWriter->getIndexFromAddress(LowPCToUse, Unit);
|
|
DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
|
|
LowPCAttrInfo.getForm(), DIEInteger(Index));
|
|
} else
|
|
DIEBldr.replaceValue(&Die, LowPCAttrInfo.getAttribute(),
|
|
LowPCAttrInfo.getForm(), DIEInteger(LowPCToUse));
|
|
|
|
// Original CU didn't have DW_AT_*_base. We converted it's children (or
|
|
// dwo), so need to insert it into CU.
|
|
if (RangesBase)
|
|
DIEBldr.addValue(&Die, RangeBaseAttribute, dwarf::DW_FORM_sec_offset,
|
|
DIEInteger(*RangesBase));
|
|
}
|
|
|
|
uint64_t RangeAttrVal = RangesSectionOffset - BaseOffset;
|
|
if (Unit.getVersion() >= 5)
|
|
RangeAttrVal = RangesSectionOffset;
|
|
// HighPC was conveted into DW_AT_ranges.
|
|
// For DWARF5 we only access ranges throught index.
|
|
|
|
DIEBldr.replaceValue(&Die, HighPCAttrInfo.getAttribute(), dwarf::DW_AT_ranges,
|
|
RangesForm, DIEInteger(RangeAttrVal));
|
|
}
|