Rebase: [BOLT] DebugFission Support

Summary:
Implemented support for Debug Fission.
For the most part it doesn't impact Monolithic execution path.
One area that was changed is the DW_AT_low_pc/DW_AT_high_pc conversion. Before it was to DW_AT_ranges/DW_AT_low_pc, now DW_AT_low_pc is kept in same place.
Another more visible impact is in Skeleton CU the DW_AT_low_pc is replaced with DW_AT_ranges_base if it's not originally present and bolt converted ranges conversion inside the dwo units.

Output of this are multiple .dwo files with updated debug information.

(cherry picked from FBD29569788)
This commit is contained in:
Amir Ayupov
2021-04-01 11:43:00 -07:00
committed by Maksim Panchenko
parent 99d7f90635
commit 12e9fec697
14 changed files with 1695 additions and 227 deletions

View File

@@ -50,6 +50,12 @@ extern cl::opt<unsigned> ExecutionCountThreshold;
extern bool processAllFunctions();
static cl::opt<std::string>
DWPPath("dwp-path",
cl::desc("Path to DWP file. DWP file name must be same as "
"binary name with .dwp extension."),
cl::Hidden, cl::ZeroOrMore, cl::cat(BoltCategory));
cl::opt<bool>
NoHugePages("no-huge-pages",
cl::desc("use regular size pages for code alignment"),
@@ -161,7 +167,7 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
/// Create BinaryContext for a given architecture \p ArchName and
/// triple \p TripleName.
std::unique_ptr<BinaryContext>
BinaryContext::createBinaryContext(ObjectFile *File, bool IsPIC,
BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
std::unique_ptr<DWARFContext> DwCtx) {
StringRef ArchName = "";
StringRef FeaturesStr = "";
@@ -1451,6 +1457,44 @@ std::vector<BinaryFunction *> BinaryContext::getAllBinaryFunctions() {
return AllFunctions;
}
Optional<DWARFUnit *> BinaryContext::getDWOCU(uint64_t DWOId) {
auto Iter = DWOCUs.find(DWOId);
if (Iter == DWOCUs.end())
return None;
return Iter->second;
}
/// Handles DWO sections that can either be in .o, .dwo or .dwp files.
void BinaryContext::preprocessDWODebugInfo() {
// If DWP file exists use it to populate CU map.
if (!opts::DWPPath.empty()) {
DWPContext = DwCtx->getDWOContext(opts::DWPPath.c_str());
if (!DWPContext)
report_error("DWP file not found.",
std::make_error_code(std::errc::no_such_file_or_directory));
for (const std::unique_ptr<DWARFUnit> &CU : DwCtx->compile_units()) {
DWARFUnit *DwarfUnit = CU.get();
if (llvm::Optional<uint64_t> DWOId = DwarfUnit->getDWOId()) {
DWARFCompileUnit *DWOCU =
DWPContext.get()->getDWOCompileUnitForHash(*DWOId);
if (DWOCU)
DWOCUs[*DWOId] = DWOCU;
}
}
} else {
for (const std::unique_ptr<DWARFUnit> &CU : DwCtx->compile_units()) {
DWARFUnit *const DwarfUnit = CU.get();
if (llvm::Optional<uint64_t> DWOId = DwarfUnit->getDWOId()) {
DWARFUnit *DWOCU =
DwarfUnit->getNonSkeletonUnitDIE(false).getDwarfUnit();
assert(DWOCU->isDWOUnit() && "No CU for DWO ID.");
DWOCUs[*DWOId] = DWOCU;
}
}
}
}
void BinaryContext::preprocessDebugInfo() {
struct CURange {
uint64_t LowPC;
@@ -1528,6 +1572,8 @@ void BinaryContext::preprocessDebugInfo() {
cantFail(Ctx->getDwarfFile(Dir, FileName, 0, None, None, CUID));
}
}
preprocessDWODebugInfo();
}
bool BinaryContext::shouldEmit(const BinaryFunction &Function) const {

View File

@@ -30,6 +30,7 @@
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCSymbol.h"
@@ -195,11 +196,27 @@ class BinaryContext {
/// The runtime library.
std::unique_ptr<RuntimeLibrary> RtLibrary;
/// DWP Context.
std::shared_ptr<DWARFContext> DWPContext;
/// A map of DWO Ids to CUs.
using DWOIdToCUMapType = std::unordered_map<uint64_t, DWARFUnit *>;
DWOIdToCUMapType DWOCUs;
/// Preprocess DWO debug information.
void preprocessDWODebugInfo();
public:
static std::unique_ptr<BinaryContext>
createBinaryContext(ObjectFile *File, bool IsPIC,
createBinaryContext(const ObjectFile *File, bool IsPIC,
std::unique_ptr<DWARFContext> DwCtx);
/// Given DWOId returns CU if it existss in DWOCUs.
Optional<DWARFUnit *> getDWOCU(uint64_t DWOId);
/// Get Number of DWOCUs in a map.
uint32_t getNumDWOCUs() { return DWOCUs.size(); }
/// [start memory address] -> [segment info] mapping.
std::map<uint64_t, SegmentInfo> SegmentMapInfo;
@@ -1193,6 +1210,22 @@ public:
TheTarget->createMCCodeEmitter(*MII, *MRI, *MCEInstance.LocalCtx));
return MCEInstance;
}
/// Creating MCStreamer instance.
std::unique_ptr<MCStreamer>
createStreamer(llvm::raw_pwrite_stream &OS) const {
MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *Ctx);
MCAsmBackend *MAB =
TheTarget->createMCAsmBackend(*STI, *MRI, MCTargetOptions());
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);
std::unique_ptr<MCStreamer> Streamer(TheTarget->createMCObjectStreamer(
*TheTriple, *Ctx, std::unique_ptr<MCAsmBackend>(MAB), std::move(OW),
std::unique_ptr<MCCodeEmitter>(MCE), *STI,
/* RelaxAll */ false,
/* IncrementalLinkerCompatible */ false,
/* DWARFMustBeAtTheEnd */ false));
return Streamer;
}
};
template <typename T,

View File

@@ -12,6 +12,7 @@
#define LLVM_TOOLS_LLVM_BOLT_BINARY_DATA_H
#include "llvm/ADT/Twine.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

View File

@@ -25,6 +25,7 @@
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"

View File

@@ -23,18 +23,34 @@
#include "llvm/MC/MCDwarf.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/ToolOutputFile.h"
#include <algorithm>
#include <cstdint>
#include <string>
#include <unordered_map>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt"
LLVM_ATTRIBUTE_UNUSED
static void printDie(const DWARFDie &DIE) {
DIDumpOptions DumpOpts;
DumpOpts.ShowForm = true;
DumpOpts.Verbose = true;
DumpOpts.ChildRecurseDepth = 0;
DumpOpts.ShowChildren = 0;
DIE.dump(dbgs(), 0, DumpOpts);
}
using namespace llvm;
using namespace llvm::support::endian;
using namespace object;
@@ -59,40 +75,205 @@ DeterministicDebugInfo("deterministic-debuginfo",
cl::init(true),
cl::cat(BoltCategory));
static cl::opt<std::string>
DwoOutputPath("dwo-output-path",
cl::desc("Path to where .dwo files will be written out to."),
cl::init(""), cl::cat(BoltCategory));
static cl::opt<bool>
DebugSkeletonCu("debug-skeleton-cu",
cl::desc("Prints out offsetrs for abbrev and debu_info of "
"Skeleton CUs that get patched."),
cl::ZeroOrMore, cl::Hidden, cl::init(false),
cl::cat(BoltCategory));
} // namespace opts
/// Returns DWO Name to be used. Handles case where user specifies output DWO
/// directory, and there are duplicate names. Assumes DWO ID is unique.
static std::string
getDWOName(llvm::DWARFUnit &CU,
std::unordered_map<std::string, uint32_t> *NameToIndexMap,
std::unordered_map<uint64_t, std::string> &DWOIdToName) {
llvm::Optional<uint64_t> DWOId = CU.getDWOId();
assert(DWOId && "DWO ID not found.");
(void)DWOId;
auto NameIter = DWOIdToName.find(*DWOId);
if (NameIter != DWOIdToName.end())
return NameIter->second;
std::string DWOName = dwarf::toString(
CU.getUnitDIE().find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}),
"");
assert(!DWOName.empty() &&
"DW_AT_dwo_name/DW_AT_GNU_dwo_name does not exists.");
if (NameToIndexMap && !opts::DwoOutputPath.empty()) {
auto Iter = NameToIndexMap->find(DWOName);
if (Iter == NameToIndexMap->end()) {
Iter = NameToIndexMap->insert({DWOName, 0}).first;
}
DWOName.append(std::to_string(Iter->second));
++Iter->second;
}
DWOName.append(".dwo");
DWOIdToName[*DWOId] = DWOName;
return DWOName;
}
static bool isHighPcFormEightBytes(dwarf::Form DwarfForm) {
return DwarfForm == dwarf::DW_FORM_addr || DwarfForm == dwarf::DW_FORM_data8;
}
void DWARFRewriter::updateDebugInfo() {
ErrorOr<BinarySection &> DebugAbbrev =
BC.getUniqueSectionByName(".debug_abbrev");
ErrorOr<BinarySection &> DebugInfo = BC.getUniqueSectionByName(".debug_info");
if (DebugAbbrev) {
DebugAbbrev->registerPatcher(std::make_unique<DebugAbbrevPatcher>());
AbbrevPatcher =
static_cast<DebugAbbrevPatcher *>(DebugAbbrev->getPatcher());
}
if (DebugInfo) {
DebugInfo->registerPatcher(std::make_unique<SimpleBinaryPatcher>());
DebugInfoPatcher =
static_cast<SimpleBinaryPatcher *>(DebugInfo->getPatcher());
}
if (!DebugAbbrev || !DebugInfo)
return;
DebugAbbrev->registerPatcher(std::make_unique<DebugAbbrevPatcher>());
auto *AbbrevPatcher =
static_cast<DebugAbbrevPatcher *>(DebugAbbrev->getPatcher());
DebugInfo->registerPatcher(std::make_unique<SimpleBinaryPatcher>());
auto *DebugInfoPatcher =
static_cast<SimpleBinaryPatcher *>(DebugInfo->getPatcher());
ARangesSectionWriter = std::make_unique<DebugARangesSectionWriter>();
RangesSectionWriter = std::make_unique<DebugRangesSectionWriter>(&BC);
RangesSectionWriter = std::make_unique<DebugRangesSectionWriter>();
StrWriter = std::make_unique<DebugStrWriter>(&BC);
size_t NumCUs = BC.DwCtx->getNumCompileUnits();
if (opts::NoThreads || opts::DeterministicDebugInfo) {
AddrWriter = std::make_unique<DebugAddrWriter>(&BC);
DebugLoclistWriter::setAddressWriter(AddrWriter.get());
uint64_t NumCUs = BC.DwCtx->getNumCompileUnits();
if ((opts::NoThreads || opts::DeterministicDebugInfo) &&
BC.getNumDWOCUs() == 0) {
// Use single entry for efficiency when running single-threaded
NumCUs = 1;
}
LocListWritersByCU.resize(NumCUs);
LocListWritersByCU.reserve(NumCUs);
for (size_t CUIndex = 0; CUIndex < NumCUs; ++CUIndex) {
LocListWritersByCU[CUIndex] = std::make_unique<DebugLocWriter>(&BC);
}
// Unordered maps to handle name collision if output DWO directory is
// specified.
std::unordered_map<std::string, uint32_t> NameToIndexMap;
std::unordered_map<uint64_t, std::string> DWOIdToName;
auto updateDWONameCompDir = [&](DWARFUnit &Unit) -> void {
const DWARFDie &DIE = Unit.getUnitDIE();
uint64_t AttrOffset = 0;
Optional<DWARFFormValue> ValDwoName =
DIE.find(dwarf::DW_AT_GNU_dwo_name, &AttrOffset);
assert(ValDwoName && "Skeleton CU doesn't have dwo_name.");
std::string ObjectName = getDWOName(Unit, &NameToIndexMap, DWOIdToName);
uint32_t NewOffset = StrWriter->addString(ObjectName.c_str());
DebugInfoPatcher->addLE32Patch(AttrOffset, NewOffset);
Optional<DWARFFormValue> ValCompDir =
DIE.find(dwarf::DW_AT_comp_dir, &AttrOffset);
assert(ValCompDir && "DW_AT_comp_dir is not in Skeleton CU.");
if (!opts::DwoOutputPath.empty()) {
uint32_t NewOffset = StrWriter->addString(opts::DwoOutputPath.c_str());
DebugInfoPatcher->addLE32Patch(AttrOffset, NewOffset);
}
};
uint32_t AbbrevOffsetModifier = 0;
// Case 1) Range_base found: patch .debug_info
// Case 2) Range_base not found, but Ranges will be used: patch
// .debug_info/.debug_abbrev
auto updateRangeBase = [&](DWARFUnit &Unit, uint64_t RangeBase,
bool WasRangeBaseUsed) -> void {
uint64_t AttrOffset = 0;
DWARFDie DIE = Unit.getUnitDIE();
Optional<DWARFFormValue> ValRangeBase =
DIE.find(dwarf::DW_AT_GNU_ranges_base, &AttrOffset);
bool NeedToPatch = ValRangeBase.hasValue();
uint32_t PrevAbbrevOffsetModifier = AbbrevOffsetModifier;
// Case where Skeleton CU doesn't have DW_AT_GNU_ranges_base
if (!NeedToPatch && WasRangeBaseUsed) {
const DWARFAbbreviationDeclaration *AbbreviationDecl =
DIE.getAbbreviationDeclarationPtr();
if (Optional<DWARFFormValue> ValLowPC =
DIE.find(dwarf::DW_AT_low_pc, &AttrOffset)) {
Optional<DWARFFormValue> ValHighPC = DIE.find(dwarf::DW_AT_high_pc);
uint32_t NumBytesToFill = 7;
AbbrevPatcher->addAttributePatch(AbbreviationDecl, dwarf::DW_AT_low_pc,
dwarf::DW_AT_GNU_ranges_base,
dwarf::DW_FORM_indirect);
// Bolt converts DW_AT_low_pc/DW_AT_high_pc to DW_AT_low_pc/DW_at_ranges
// DW_AT_high_pc can be 4 or 8 bytes. If it's 8 bytes need to use first
// 4 bytes.
if (ValHighPC && isHighPcFormEightBytes(ValHighPC->getForm())) {
NumBytesToFill += 4;
}
LLVM_DEBUG(if (opts::DebugSkeletonCu) dbgs()
<< "AttrOffset: " << Twine::utohexstr(AttrOffset) << "\n"
<< "Die Offset: " << Twine::utohexstr(DIE.getOffset())
<< "\n"
<< "AbbrDecl offfset: "
<< Twine::utohexstr(Unit.getAbbrOffset()) << "\n"
<< "Unit Offset: " << Twine::utohexstr(Unit.getOffset())
<< "\n\n";);
DebugInfoPatcher->addUDataPatch(AttrOffset, dwarf::DW_FORM_udata, 1);
DebugInfoPatcher->addUDataPatch(AttrOffset + 1, RangeBase,
NumBytesToFill);
// 1 Byte for DW_AT_GNU_ranges_base (since it's 2 bytes vs DW_AT_low_pc)
AbbrevOffsetModifier += 1;
} else {
errs() << "BOLT-WARNING: Skeleton CU at 0x"
<< Twine::utohexstr(DIE.getOffset())
<< " doesn't have DW_AT_GNU_ranges_base, or "
"DW_AT_low_pc to convert\n";
return;
}
}
if (NeedToPatch)
DebugInfoPatcher->addLE32Patch(AttrOffset,
static_cast<uint32_t>(RangeBase));
// DWARF4
// unit_length - 4 bytes
// version - 2 bytes
// So + 6 to patch debug_abbrev_offset
if (PrevAbbrevOffsetModifier)
DebugInfoPatcher->addLE32Patch(
Unit.getOffset() + 6, static_cast<uint32_t>(Unit.getAbbrOffset()) +
PrevAbbrevOffsetModifier);
};
auto processUnitDIE = [&](size_t CUIndex, DWARFUnit *Unit) {
updateUnitDebugInfo(CUIndex, Unit);
uint64_t RangeBase = RangesSectionWriter->getSectionOffset();
updateUnitDebugInfo(CUIndex, *Unit, *DebugInfoPatcher, *AbbrevPatcher);
if (llvm::Optional<uint64_t> DWOId = Unit->getDWOId()) {
Optional<DWARFUnit *> CU = BC.getDWOCU(*DWOId);
if (CU) {
updateDWONameCompDir(*Unit);
// Assuming there is unique DWOID per binary. i.e. Two or more CUs don't
// have same DWO ID.
assert(LocListWritersByCU.count(*DWOId) == 0 &&
"LocList writer for DWO unit already exists.");
LocListWritersByCU[*DWOId] =
std::make_unique<DebugLoclistWriter>(&BC, *DWOId);
SimpleBinaryPatcher *DwoDebugInfoPatcher =
getBinaryDWODebugInfoPatcher(*DWOId);
DwoDebugInfoPatcher->setRangeBase(RangeBase);
updateUnitDebugInfo(*DWOId, *(*CU), *DwoDebugInfoPatcher,
*getBinaryDWOAbbrevPatcher(*DWOId));
static_cast<DebugLoclistWriter *>(LocListWritersByCU[*DWOId].get())
->finalizePatches();
updateRangeBase(*Unit, RangeBase,
DwoDebugInfoPatcher->getWasRangBasedUsed());
}
}
};
if (opts::NoThreads || opts::DeterministicDebugInfo) {
@@ -111,24 +292,31 @@ void DWARFRewriter::updateDebugInfo() {
ThreadPool.wait();
}
flushPendingRanges();
flushPendingRanges(*DebugInfoPatcher);
finalizeDebugSections();
finalizeDebugSections(*DebugInfoPatcher);
writeOutDWOFiles(DWOIdToName);
updateGdbIndexSection();
}
void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
void DWARFRewriter::updateUnitDebugInfo(uint64_t CUIndex, DWARFUnit &Unit,
SimpleBinaryPatcher &DebugInfoPatcher,
DebugAbbrevPatcher &AbbrevPatcher) {
// 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();
auto &DebugLocWriter = *LocListWritersByCU[CUIndex].get();
uint64_t DIEOffset = Unit.getOffset() + Unit.getHeaderSize();
uint64_t NextCUOffset = Unit.getNextUnitOffset();
DWARFDebugInfoEntry Die;
DWARFDataExtractor DebugInfoData = Unit->getDebugInfoExtractor();
DWARFDataExtractor DebugInfoData = Unit.getDebugInfoExtractor();
uint32_t Depth = 0;
while (Die.extractFast(*Unit, &DIEOffset, DebugInfoData, NextCUOffset,
Depth)) {
while (
Die.extractFast(Unit, &DIEOffset, DebugInfoData, NextCUOffset, Depth)) {
if (const DWARFAbbreviationDeclaration *AbbrDecl =
Die.getAbbreviationDeclarationPtr()) {
if (AbbrDecl->hasChildren())
@@ -141,7 +329,8 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
break;
}
DWARFDie DIE(Unit, &Die);
DWARFDie DIE(&Unit, &Die);
switch (DIE.getTag()) {
case dwarf::DW_TAG_compile_unit: {
auto ModuleRangesOrError = DIE.getAddressRanges();
@@ -154,9 +343,10 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
BC.translateModuleAddressRanges(ModuleRanges);
const uint64_t RangesSectionOffset =
RangesSectionWriter->addRanges(OutputRanges);
ARangesSectionWriter->addCURanges(Unit->getOffset(),
ARangesSectionWriter->addCURanges(Unit.getOffset(),
std::move(OutputRanges));
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset);
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher,
AbbrevPatcher);
break;
}
case dwarf::DW_TAG_subprogram: {
@@ -187,11 +377,11 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
if (const BinaryFunction *Function =
BC.getBinaryFunctionAtAddress(Address))
FunctionRanges = Function->getOutputAddressRanges();
// Update ranges.
if (UsesRanges) {
updateDWARFObjectAddressRanges(DIE,
RangesSectionWriter->addRanges(FunctionRanges));
updateDWARFObjectAddressRanges(
DIE, RangesSectionWriter->addRanges(FunctionRanges),
DebugInfoPatcher, AbbrevPatcher);
} else {
// Delay conversion of [LowPC, HighPC) into DW_AT_ranges if possible.
const DWARFAbbreviationDeclaration *Abbrev =
@@ -203,20 +393,19 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
std::unique_lock<std::shared_timed_mutex> Lock(CriticalSectionMutex);
if (FunctionRanges.size() > 1) {
convertPending(Abbrev);
convertPending(Abbrev, DebugInfoPatcher, AbbrevPatcher);
// Exit critical section early.
Lock.unlock();
convertToRanges(DIE, FunctionRanges);
convertToRanges(DIE, FunctionRanges, DebugInfoPatcher);
} else if (ConvertedRangesAbbrevs.find(Abbrev) !=
ConvertedRangesAbbrevs.end()) {
// Exit critical section early.
Lock.unlock();
convertToRanges(DIE, FunctionRanges);
convertToRanges(DIE, FunctionRanges, DebugInfoPatcher);
} else {
if (FunctionRanges.empty())
FunctionRanges.emplace_back(DebugAddressRange());
PendingRanges[Abbrev].emplace_back(
std::make_pair(DWARFDieWrapper(DIE), FunctionRanges.front()));
addToPendingRanges(Abbrev, DIE, FunctionRanges, Unit.getDWOId());
}
}
break;
@@ -234,20 +423,18 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
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';
}
);
LLVM_DEBUG(if (OutputRanges.empty() != RangesOrError->empty()) {
dbgs() << "BOLT-DEBUG: problem with DIE at 0x"
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
<< Twine::utohexstr(Unit.getOffset()) << '\n';
});
RangesSectionOffset = RangesSectionWriter->addRanges(
std::move(OutputRanges), CachedRanges);
} else if (!RangesOrError) {
consumeError(RangesOrError.takeError());
}
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset);
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset, DebugInfoPatcher,
AbbrevPatcher);
break;
}
default: {
@@ -263,11 +450,18 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
? Value.getAsUnsignedConstant().getValue()
: Value.getAsSectionOffset().getValue();
DebugLocationsVector InputLL;
uint64_t BaseAddress = Unit->getBaseAddress()->Address;
Error E = Unit->getLocationTable().visitLocationList(
&Offset,
[&](const DWARFLocationEntry &Entry) {
Optional<object::SectionedAddress> SectionAddress =
Unit.getBaseAddress();
uint64_t BaseAddress = 0;
if (SectionAddress)
BaseAddress = SectionAddress->Address;
Error E = Unit.getLocationTable().visitLocationList(
&Offset, [&](const DWARFLocationEntry &Entry) {
switch (Entry.Kind) {
default:
llvm_unreachable("Unsupported DWARFLocationEntry Kind.");
case dwarf::DW_LLE_end_of_list:
return false;
case dwarf::DW_LLE_base_address:
@@ -276,23 +470,34 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
BaseAddress = Entry.Value0;
break;
case dwarf::DW_LLE_offset_pair:
assert(Entry.SectionIndex == SectionedAddress::UndefSection &&
"absolute address expected");
assert(
(Entry.SectionIndex == SectionedAddress::UndefSection &&
!Unit.isDWOUnit()) &&
"absolute address expected");
InputLL.emplace_back(DebugLocationEntry{
BaseAddress + Entry.Value0,
BaseAddress + Entry.Value1,
BaseAddress + Entry.Value0, BaseAddress + Entry.Value1,
Entry.Loc});
break;
case dwarf::DW_LLE_startx_length:
assert(Unit.isDWOUnit() &&
"None DWO Unit with DW_LLE_startx_length encoding.");
Optional<object::SectionedAddress> EntryAddress =
Unit.getAddrOffsetSectionItem(Entry.Value0);
assert(EntryAddress && "Address does not exist.");
InputLL.emplace_back(DebugLocationEntry{
EntryAddress->Address,
EntryAddress->Address + Entry.Value1, Entry.Loc});
break;
}
return true;
});
uint64_t OutputLocListOffset = DebugLocWriter::EmptyListTag;
if (E || InputLL.empty()) {
errs() << "BOLT-WARNING: empty location list detected at 0x"
<< Twine::utohexstr(Offset) << " for DIE at 0x"
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
<< Twine::utohexstr(Unit->getOffset())
<< '\n';
<< Twine::utohexstr(Unit.getOffset()) << '\n';
} else {
const uint64_t Address = InputLL.front().LowPC;
if (const BinaryFunction *Function =
@@ -303,27 +508,54 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
dbgs() << "BOLT-DEBUG: location list translated to an empty "
"one at 0x"
<< Twine::utohexstr(DIE.getOffset()) << " in CU at 0x"
<< Twine::utohexstr(Unit->getOffset())
<< '\n';
<< Twine::utohexstr(Unit.getOffset()) << '\n';
});
OutputLocListOffset =
LocListWritersByCU[CUIndex]->addList(OutputLL);
OutputLocListOffset = DebugLocWriter.addList(OutputLL);
}
}
if (OutputLocListOffset != DebugLocWriter::EmptyListTag) {
std::lock_guard<std::mutex> Lock(LocListDebugInfoPatchesMutex);
LocListDebugInfoPatches.push_back(
{AttrOffset, CUIndex, OutputLocListOffset});
if (Unit.isDWOUnit()) {
// Not sure if better approach is to hide all of this away in a
// class. Also re-using LocListDebugInfoPatchType. Wasting some
// space for DWOID/CUIndex.
DwoLocListDebugInfoPatches[CUIndex].push_back(
{AttrOffset, CUIndex, OutputLocListOffset});
} else {
LocListDebugInfoPatches.push_back(
{AttrOffset, CUIndex, OutputLocListOffset});
}
} else {
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
DebugInfoPatcher->addLE32Patch(AttrOffset,
DebugLocWriter::EmptyListOffset);
DebugInfoPatcher.addLE32Patch(AttrOffset,
DebugLocWriter::EmptyListOffset);
}
} else {
assert((Value.isFormClass(DWARFFormValue::FC_Exprloc) ||
Value.isFormClass(DWARFFormValue::FC_Block)) &&
"unexpected DW_AT_location form");
if (Unit.isDWOUnit()) {
ArrayRef<uint8_t> Expr = *Value.getAsBlock();
DataExtractor Data(
StringRef((const char *)Expr.data(), Expr.size()),
Unit.getContext().isLittleEndian(), 0);
DWARFExpression LocExpr(Data, Unit.getAddressByteSize(),
Unit.getFormParams().Format);
for (auto &Expr : LocExpr) {
if (Expr.getCode() != dwarf::DW_OP_GNU_addr_index)
continue;
uint64_t Index = Expr.getRawOperand(0);
Optional<object::SectionedAddress> EntryAddress =
Unit.getAddrOffsetSectionItem(Index);
assert(EntryAddress && "Address is not found.");
assert(Index <= std::numeric_limits<uint32_t>::max() &&
"Invalid Operand Index.");
AddrWriter->addIndexAddress(EntryAddress->Address,
static_cast<uint32_t>(Index),
*Unit.getDWOId());
}
}
}
} else if (Optional<DWARFFormValue> V =
DIE.find(dwarf::DW_AT_low_pc, &AttrOffset)) {
@@ -342,8 +574,23 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
<< Twine::utohexstr(NewAddress) << '\n');
}
dwarf::Form Form = Value.getForm();
assert(Form != dwarf::DW_FORM_LLVM_addrx_offset &&
"DW_FORM_LLVM_addrx_offset is not supported");
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
DebugInfoPatcher->addLE64Patch(AttrOffset, NewAddress);
if (Form == dwarf::DW_FORM_GNU_addr_index) {
assert(Unit.isDWOUnit() &&
"DW_FORM_GNU_addr_index in Non DWO unit.");
uint64_t Index = Value.getRawUValue();
// If there is no new address, storing old address.
// Re-using Index to make implementation easier.
// DW_FORM_GNU_addr_index is variable lenght encoding so we either
// have to create indices of same sizes, or use same index.
AddrWriter->addIndexAddress(NewAddress ? NewAddress : Address,
Index, *Unit.getDWOId());
} else {
DebugInfoPatcher.addLE64Patch(AttrOffset, NewAddress);
}
} else if (opts::Verbosity >= 1) {
errs() << "BOLT-WARNING: unexpected form value for attribute at 0x"
<< Twine::utohexstr(AttrOffset);
@@ -355,12 +602,13 @@ void DWARFRewriter::updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit) {
if (DIEOffset > NextCUOffset) {
errs() << "BOLT-WARNING: corrupt DWARF detected at 0x"
<< Twine::utohexstr(Unit->getOffset()) << '\n';
<< Twine::utohexstr(Unit.getOffset()) << '\n';
}
}
void DWARFRewriter::updateDWARFObjectAddressRanges(
const DWARFDie DIE, uint64_t DebugRangesOffset) {
const DWARFDie DIE, uint64_t DebugRangesOffset,
SimpleBinaryPatcher &DebugInfoPatcher, DebugAbbrevPatcher &AbbrevPatcher) {
// Some objects don't have an associated DIE and cannot be updated (such as
// compiler-generated functions).
@@ -384,10 +632,11 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
// In this case we simply need to update the value of DW_AT_ranges.
uint64_t AttrOffset = -1U;
DIE.find(dwarf::DW_AT_ranges, &AttrOffset);
assert(AttrOffset != -1U && "failed to locate DWARF attribute");
assert(AttrOffset != -1U && "failed to locate DWARF attribute");
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
DebugInfoPatcher->addLE32Patch(AttrOffset, DebugRangesOffset);
DebugInfoPatcher.addLE32Patch(
AttrOffset, DebugRangesOffset - DebugInfoPatcher.getRangeBase());
} else {
// Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back
// to back. We replace the attributes with DW_AT_ranges and DW_AT_low_pc.
@@ -403,8 +652,8 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
// large size.
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_low_pc) &&
AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_high_pc)) {
convertToRanges(AbbreviationDecl);
convertToRanges(DIE, DebugRangesOffset);
convertToRanges(AbbreviationDecl, AbbrevPatcher);
convertToRanges(DIE, DebugRangesOffset, DebugInfoPatcher);
} else {
if (opts::Verbosity >= 1) {
errs() << "BOLT-WARNING: Cannot update ranges for DIE at offset 0x"
@@ -516,7 +765,8 @@ void DWARFRewriter::updateLineTableOffsets() {
TypeInfoSection->setIsFinalized();
}
void DWARFRewriter::finalizeDebugSections() {
void DWARFRewriter::finalizeDebugSections(
SimpleBinaryPatcher &DebugInfoPatcher) {
// Skip .debug_aranges if we are re-generating .gdb_index.
if (opts::KeepARanges || !BC.getGdbIndexSection()) {
SmallVector<char, 16> ARangesBuffer;
@@ -533,6 +783,31 @@ void DWARFRewriter::finalizeDebugSections() {
ARangesContents.size());
}
if (StrWriter->isInitialized()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
StrWriter->finalize();
BC.registerOrUpdateNoteSection(".debug_str",
copyByteArray(*DebugStrSectionContents),
DebugStrSectionContents->size());
}
if (AddrWriter->isInitialized()) {
AddressSectionBuffer AddressSectionContents = AddrWriter->finalize();
BC.registerOrUpdateNoteSection(".debug_addr",
copyByteArray(AddressSectionContents),
AddressSectionContents.size());
for (auto &CU : BC.DwCtx->compile_units()) {
DWARFDie DIE = CU->getUnitDIE();
uint64_t AttrOffset = 0;
if (Optional<DWARFFormValue> Val =
DIE.find(dwarf::DW_AT_GNU_addr_base, &AttrOffset)) {
uint64_t Offset = AddrWriter->getOffset(*CU->getDWOId());
DebugInfoPatcher.addLE32Patch(AttrOffset, static_cast<int32_t>(Offset));
}
}
}
std::unique_ptr<RangesBufferVector> RangesSectionContents =
RangesSectionWriter->finalize();
BC.registerOrUpdateNoteSection(".debug_ranges",
@@ -540,12 +815,93 @@ void DWARFRewriter::finalizeDebugSections() {
RangesSectionContents->size());
std::unique_ptr<LocBufferVector> LocationListSectionContents =
makeFinalLocListsSection();
makeFinalLocListsSection(DebugInfoPatcher);
BC.registerOrUpdateNoteSection(".debug_loc",
copyByteArray(*LocationListSectionContents),
LocationListSectionContents->size());
}
void DWARFRewriter::writeOutDWOFiles(
std::unordered_map<uint64_t, std::string> &DWOIdToName) {
std::string DebugData = "";
auto ApplyPatch = [&](BinaryPatcher *Patcher, StringRef Data) -> StringRef {
DebugData = Data.str();
Patcher->patchBinary(DebugData);
return StringRef(DebugData.c_str(), DebugData.size());
};
for (const std::unique_ptr<DWARFUnit> &CU : BC.DwCtx->compile_units()) {
Optional<uint64_t> DWOId = CU->getDWOId();
if (!DWOId)
continue;
Optional<DWARFUnit *> DWOCU = BC.getDWOCU(*DWOId);
if (!DWOCU)
continue;
const object::ObjectFile *File =
(*DWOCU)->getContext().getDWARFObj().getFile();
std::string CompDir = opts::DwoOutputPath.empty()
? CU->getCompilationDir()
: opts::DwoOutputPath.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);
std::unique_ptr<BinaryContext> TmpBC = BinaryContext::createBinaryContext(
File, false,
DWARFContext::create(*File, nullptr, "", WithColor::defaultErrorHandler,
WithColor::defaultWarningHandler,
/*UsesRelocs=*/false));
std::unique_ptr<MCStreamer> Streamer = TmpBC->createStreamer(TempOut->os());
const MCObjectFileInfo &MCOFI = *Streamer->getContext().getObjectFileInfo();
const StringMap<MCSection *> KnownSections = {
{".debug_info.dwo", MCOFI.getDwarfInfoDWOSection()},
{".debug_types.dwo", MCOFI.getDwarfTypesDWOSection()},
{".debug_str.dwo", MCOFI.getDwarfStrDWOSection()},
{".debug_str_offsets.dwo", MCOFI.getDwarfStrOffDWOSection()},
{".debug_abbrev.dwo", MCOFI.getDwarfAbbrevDWOSection()},
{".debug_loc.dwo", MCOFI.getDwarfLocDWOSection()},
{".debug_line.dwo", MCOFI.getDwarfLineDWOSection()}};
for (const SectionRef &Section : File->sections()) {
Expected<StringRef> SectionName = Section.getName();
assert(SectionName && "Invalid section name.");
auto SectionIter = KnownSections.find(*SectionName);
if (SectionIter == KnownSections.end())
continue;
Streamer->SwitchSection(SectionIter->second);
Expected<StringRef> Contents = Section.getContents();
assert(Contents && "Invalid contents.");
StringRef OutData(*Contents);
std::unique_ptr<LocBufferVector> Data;
if (SectionName->equals(".debug_loc.dwo")) {
DebugLocWriter *LocWriter = LocListWritersByCU[*DWOId].get();
Data = LocWriter->finalize();
// Creating explicit with creating of StringRef here, otherwise
// with impicit conversion it will take null byte as end of
// string.
OutData = StringRef(reinterpret_cast<const char *>(Data->data()),
Data->size());
} else if (SectionName->equals(".debug_info.dwo")) {
SimpleBinaryPatcher *Patcher = getBinaryDWODebugInfoPatcher(*DWOId);
OutData = ApplyPatch(Patcher, OutData);
} else if (SectionName->equals(".debug_abbrev.dwo")) {
DebugAbbrevPatcher *Patcher = getBinaryDWOAbbrevPatcher(*DWOId);
OutData = ApplyPatch(Patcher, OutData);
}
Streamer->emitBytes(OutData);
}
Streamer->Finish();
TempOut->keep();
}
}
void DWARFRewriter::updateGdbIndexSection() {
if (!BC.getGdbIndexSection())
return;
@@ -655,36 +1011,30 @@ void DWARFRewriter::updateGdbIndexSection() {
NewGdbIndexSize);
}
void
DWARFRewriter::convertToRanges(const DWARFAbbreviationDeclaration *Abbrev) {
if (!AbbrevPatcher)
return;
void DWARFRewriter::convertToRanges(const DWARFAbbreviationDeclaration *Abbrev,
DebugAbbrevPatcher &AbbrevPatcher) {
dwarf::Form HighPCForm = Abbrev->findAttribute(dwarf::DW_AT_high_pc)->Form;
dwarf::Form LowPCForm = Abbrev->findAttribute(dwarf::DW_AT_low_pc)->Form;
std::lock_guard<std::mutex> Lock(AbbrevPatcherMutex);
AbbrevPatcher->addAttributePatch(Abbrev,
dwarf::DW_AT_low_pc,
dwarf::DW_AT_ranges,
dwarf::DW_FORM_sec_offset);
if (HighPCForm == dwarf::DW_FORM_addr ||
HighPCForm == dwarf::DW_FORM_data8) {
// LowPC must have 12 bytes, use indirect
AbbrevPatcher->addAttributePatch(Abbrev,
dwarf::DW_AT_high_pc,
dwarf::DW_AT_low_pc,
dwarf::DW_FORM_indirect);
} else if (HighPCForm == dwarf::DW_FORM_data4) {
// LowPC must have 8 bytes, use addr
AbbrevPatcher->addAttributePatch(Abbrev,
dwarf::DW_AT_high_pc,
dwarf::DW_AT_low_pc,
dwarf::DW_FORM_addr);
} else {
llvm_unreachable("unexpected form");
}
// DW_FORM_GNU_addr_index is already variable encoding so nothing to do there.
// If HighForm is 8 bytes need to change low_pc to be variable encoding to
// consume extra bytes from high_pc, since DW_FORM_sec_offset is 4 bytes for
// DWARF32.
if (LowPCForm != dwarf::DW_FORM_GNU_addr_index &&
isHighPcFormEightBytes(HighPCForm))
AbbrevPatcher.addAttributePatch(Abbrev, dwarf::DW_AT_low_pc,
dwarf::DW_AT_low_pc,
dwarf::DW_FORM_indirect);
AbbrevPatcher.addAttributePatch(Abbrev, dwarf::DW_AT_high_pc,
dwarf::DW_AT_ranges,
dwarf::DW_FORM_sec_offset);
}
void DWARFRewriter::convertToRanges(DWARFDie DIE,
const DebugAddressRangesVector &Ranges) {
const DebugAddressRangesVector &Ranges,
SimpleBinaryPatcher &DebugInfoPatcher) {
uint64_t RangesSectionOffset;
if (Ranges.empty()) {
RangesSectionOffset = RangesSectionWriter->getEmptyRangesOffset();
@@ -692,19 +1042,21 @@ void DWARFRewriter::convertToRanges(DWARFDie DIE,
RangesSectionOffset = RangesSectionWriter->addRanges(Ranges);
}
convertToRanges(DIE, RangesSectionOffset);
convertToRanges(DIE, RangesSectionOffset, DebugInfoPatcher);
}
void DWARFRewriter::convertPending(const DWARFAbbreviationDeclaration *Abbrev) {
void DWARFRewriter::convertPending(const DWARFAbbreviationDeclaration *Abbrev,
SimpleBinaryPatcher &DebugInfoPatcher,
DebugAbbrevPatcher &AbbrevPatcher) {
if (ConvertedRangesAbbrevs.count(Abbrev))
return;
convertToRanges(Abbrev);
convertToRanges(Abbrev, AbbrevPatcher);
auto I = PendingRanges.find(Abbrev);
if (I != PendingRanges.end()) {
for (std::pair<DWARFDieWrapper, DebugAddressRange> &Pair : I->second) {
convertToRanges(Pair.first, {Pair.second});
convertToRanges(Pair.first, {Pair.second}, DebugInfoPatcher);
}
PendingRanges.erase(I);
}
@@ -712,7 +1064,38 @@ void DWARFRewriter::convertPending(const DWARFAbbreviationDeclaration *Abbrev) {
ConvertedRangesAbbrevs.emplace(Abbrev);
}
std::unique_ptr<LocBufferVector> DWARFRewriter::makeFinalLocListsSection() {
void DWARFRewriter::addToPendingRanges(
const DWARFAbbreviationDeclaration *Abbrev, DWARFDie DIE,
DebugAddressRangesVector &FunctionRanges, Optional<uint64_t> DWOId) {
Optional<DWARFFormValue> LowPcValue = DIE.find(dwarf::DW_AT_low_pc);
Optional<DWARFFormValue> HighPcValue = DIE.find(dwarf::DW_AT_high_pc);
if (LowPcValue &&
LowPcValue->getForm() == dwarf::Form::DW_FORM_GNU_addr_index) {
assert(DWOId && "Invalid DWO ID.");
(void)DWOId;
assert(HighPcValue && "Low PC exists, but not High PC.");
(void)HighPcValue;
uint64_t IndexL = LowPcValue->getRawUValue();
uint64_t IndexH = HighPcValue->getRawUValue();
for (auto Address : FunctionRanges) {
AddrWriter->addIndexAddress(Address.LowPC, IndexL, *DWOId);
// 2.17.2
// If the value of the DW_AT_high_pc is of class address, it is the
// relocated address of the first location past the last instruction
// associated with the entity; if it is of class constant, the value is
// an unsigned integer offset which when added to the low PC gives the
// address of the first location past the last instruction associated
// with the entity.
if (!HighPcValue->isFormClass(DWARFFormValue::FC_Constant))
AddrWriter->addIndexAddress(Address.HighPC, IndexH, *DWOId);
}
}
PendingRanges[Abbrev].emplace_back(
std::make_pair(DWARFDieWrapper(DIE), FunctionRanges.front()));
}
std::unique_ptr<LocBufferVector>
DWARFRewriter::makeFinalLocListsSection(SimpleBinaryPatcher &DebugInfoPatcher) {
auto LocBuffer = std::make_unique<LocBufferVector>();
auto LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
auto Writer =
@@ -725,31 +1108,48 @@ std::unique_ptr<LocBufferVector> DWARFRewriter::makeFinalLocListsSection() {
*LocStream << StringRef(Zeroes, 16);
SectionOffset += 2 * 8;
std::vector<uint64_t> SectionOffsetByCU(LocListWritersByCU.size());
std::unordered_map<uint64_t, uint64_t> SectionOffsetByCU(
LocListWritersByCU.size());
for (size_t CUIndex = 0; CUIndex < LocListWritersByCU.size(); ++CUIndex) {
for (std::pair<const uint64_t, std::unique_ptr<DebugLocWriter>> &Loc :
LocListWritersByCU) {
uint64_t CUIndex = Loc.first;
DebugLocWriter *LocWriter = Loc.second.get();
if (llvm::isa<DebugLoclistWriter>(*LocWriter))
continue;
SectionOffsetByCU[CUIndex] = SectionOffset;
std::unique_ptr<LocBufferVector> CurrCULocationLists =
LocListWritersByCU[CUIndex]->finalize();
LocWriter->finalize();
*LocStream << *CurrCULocationLists;
SectionOffset += CurrCULocationLists->size();
}
for (std::pair<const uint64_t, VectorLocListDebugInfoPatchType> &Iter :
DwoLocListDebugInfoPatches) {
uint64_t DWOId = Iter.first;
SimpleBinaryPatcher *Patcher = getBinaryDWODebugInfoPatcher(DWOId);
for (LocListDebugInfoPatchType &Patch : Iter.second) {
Patcher->addLE32Patch(Patch.DebugInfoOffset,
SectionOffsetByCU[Patch.CUIndex] +
Patch.CUWriterOffset);
}
}
for (LocListDebugInfoPatchType &Patch : LocListDebugInfoPatches) {
DebugInfoPatcher
->addLE32Patch(Patch.DebugInfoOffset,
SectionOffsetByCU[Patch.CUIndex] + Patch.CUWriterOffset);
DebugInfoPatcher.addLE32Patch(Patch.DebugInfoOffset,
SectionOffsetByCU[Patch.CUIndex] +
Patch.CUWriterOffset);
}
return LocBuffer;
}
void DWARFRewriter::flushPendingRanges() {
void DWARFRewriter::flushPendingRanges(SimpleBinaryPatcher &DebugInfoPatcher) {
for (std::pair<const DWARFAbbreviationDeclaration *const,
std::vector<std::pair<DWARFDieWrapper, DebugAddressRange>>>
&I : PendingRanges) {
for (std::pair<DWARFDieWrapper, DebugAddressRange> &RangePair : I.second) {
patchLowHigh(RangePair.first, RangePair.second);
patchLowHigh(RangePair.first, RangePair.second, DebugInfoPatcher);
}
}
clearList(PendingRanges);
@@ -766,15 +1166,17 @@ void getRangeAttrData(
LowPCFormValue = *DIE.find(dwarf::DW_AT_low_pc, &LowPCOffset);
HighPCFormValue = *DIE.find(dwarf::DW_AT_high_pc, &HighPCOffset);
if (LowPCFormValue.getForm() != dwarf::DW_FORM_addr ||
if ((LowPCFormValue.getForm() != dwarf::DW_FORM_addr &&
LowPCFormValue.getForm() != dwarf::DW_FORM_GNU_addr_index) ||
(HighPCFormValue.getForm() != dwarf::DW_FORM_addr &&
HighPCFormValue.getForm() != dwarf::DW_FORM_data8 &&
HighPCFormValue.getForm() != dwarf::DW_FORM_data4)) {
errs() << "BOLT-WARNING: unexpected form value. Cannot update DIE "
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n";
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n";
return;
}
if (LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) {
if ((LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) &&
LowPCFormValue.getForm() != dwarf::DW_FORM_GNU_addr_index) {
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
<< "Cannot update DIE at offset 0x"
<< Twine::utohexstr(DIE.getOffset()) << '\n';
@@ -784,35 +1186,43 @@ void getRangeAttrData(
}
void DWARFRewriter::patchLowHigh(DWARFDie DIE, DebugAddressRange Range) {
if (!DebugInfoPatcher)
return;
void DWARFRewriter::patchLowHigh(DWARFDie DIE, DebugAddressRange Range,
SimpleBinaryPatcher &DebugInfoPatcher) {
uint64_t LowPCOffset, HighPCOffset;
DWARFFormValue LowPCFormValue, HighPCFormValue;
getRangeAttrData(
DIE, LowPCOffset, HighPCOffset, LowPCFormValue, HighPCFormValue);
DebugInfoPatcher->addLE64Patch(LowPCOffset, Range.LowPC);
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
DebugInfoPatcher->addLE64Patch(HighPCOffset, Range.HighPC - Range.LowPC);
auto *TempDebugPatcher = &DebugInfoPatcher;
if (LowPCFormValue.getForm() == dwarf::DW_FORM_GNU_addr_index) {
DWARFUnit *Unit = DIE.getDwarfUnit();
assert(Unit->isDWOUnit() && "DW_FORM_GNU_addr_index not part of DWO.");
uint32_t AddressIndex =
AddrWriter->getIndexFromAddress(Range.LowPC, *Unit->getDWOId());
TempDebugPatcher = getBinaryDWODebugInfoPatcher(*Unit->getDWOId());
TempDebugPatcher->addUDataPatch(LowPCOffset, AddressIndex,
std::abs(int(HighPCOffset - LowPCOffset)));
// TODO: In DWARF5 support ULEB128 for high_pc
} else {
DebugInfoPatcher->addLE32Patch(HighPCOffset, Range.HighPC - Range.LowPC);
TempDebugPatcher->addLE64Patch(LowPCOffset, Range.LowPC);
}
if (isHighPcFormEightBytes(HighPCFormValue.getForm())) {
TempDebugPatcher->addLE64Patch(HighPCOffset, Range.HighPC - Range.LowPC);
} else {
TempDebugPatcher->addLE32Patch(HighPCOffset, Range.HighPC - Range.LowPC);
}
}
void DWARFRewriter::convertToRanges(DWARFDie DIE,
uint64_t RangesSectionOffset) {
if (!DebugInfoPatcher)
return;
void DWARFRewriter::convertToRanges(DWARFDie DIE, uint64_t RangesSectionOffset,
SimpleBinaryPatcher &DebugInfoPatcher) {
uint64_t LowPCOffset, HighPCOffset;
DWARFFormValue LowPCFormValue, HighPCFormValue;
getRangeAttrData(
DIE, LowPCOffset, HighPCOffset, LowPCFormValue, HighPCFormValue);
getRangeAttrData(DIE, LowPCOffset, HighPCOffset, LowPCFormValue,
HighPCFormValue);
unsigned LowPCSize = 0;
assert(DIE.getDwarfUnit()->getAddressByteSize() == 8);
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
if (isHighPcFormEightBytes(HighPCFormValue.getForm())) {
LowPCSize = 12;
} else if (HighPCFormValue.getForm() == dwarf::DW_FORM_data4) {
LowPCSize = 8;
@@ -821,13 +1231,24 @@ void DWARFRewriter::convertToRanges(DWARFDie DIE,
}
std::lock_guard<std::mutex> Lock(DebugInfoPatcherMutex);
DebugInfoPatcher->addLE32Patch(LowPCOffset, RangesSectionOffset);
if (LowPCSize == 12) {
uint32_t BaseOffset = 0;
if (LowPCFormValue.getForm() == dwarf::DW_FORM_GNU_addr_index) {
// Add Indexer is already variable length encoding.
DebugInfoPatcher.addUDataPatch(LowPCOffset, 0,
std::abs(int(HighPCOffset - LowPCOffset)) +
LowPCSize - 8);
// Ranges are relative to DW_AT_GNU_ranges_base.
BaseOffset = DebugInfoPatcher.getRangeBase();
} else if (LowPCSize == 12) {
// Creatively encoding dwarf::DW_FORM_addr in to 4 bytes.
// Write an indirect 0 value for DW_AT_low_pc so that we can fill
// 12 bytes of space (see T56239836 for more details)
DebugInfoPatcher->addUDataPatch(LowPCOffset + 4, dwarf::DW_FORM_addr, 4);
DebugInfoPatcher->addLE64Patch(LowPCOffset + 8, 0);
// 12 bytes of space.
// The Abbrev wa already changed.
DebugInfoPatcher.addUDataPatch(LowPCOffset, dwarf::DW_FORM_addr, 4);
DebugInfoPatcher.addLE64Patch(LowPCOffset + 4, 0);
} else {
DebugInfoPatcher->addLE64Patch(LowPCOffset + 4, 0);
DebugInfoPatcher.addLE64Patch(LowPCOffset, 0);
}
DebugInfoPatcher.addLE32Patch(HighPCOffset + LowPCSize - 8,
RangesSectionOffset - BaseOffset);
}

View File

@@ -12,8 +12,11 @@
#define LLVM_TOOLS_LLVM_BOLT_DWARF_REWRITER_H
#include "DebugData.h"
#include "RewriteInstance.h"
#include <cstdint>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <unordered_map>
#include <vector>
@@ -28,12 +31,8 @@ class DWARFRewriter {
BinaryContext &BC;
SimpleBinaryPatcher *DebugInfoPatcher{nullptr};
std::mutex DebugInfoPatcherMutex;
DebugAbbrevPatcher *AbbrevPatcher{nullptr};
std::mutex AbbrevPatcherMutex;
/// Stores and serializes information that will be put into the
@@ -44,23 +43,50 @@ class DWARFRewriter {
/// .debug_aranges DWARF section.
std::unique_ptr<DebugARangesSectionWriter> ARangesSectionWriter;
/// Stores and serializes information that will be put into the
/// .debug_addr DWARF section.
std::unique_ptr<DebugAddrWriter> AddrWriter;
/// Stores and serializes information that will be put in to the
/// .debug_addr DWARF section.
/// Does not do de-duplication.
std::unique_ptr<DebugStrWriter> StrWriter;
using LocWriters =
std::unordered_map<uint64_t, std::unique_ptr<DebugLocWriter>>;
/// Use a separate location list writer for each compilation unit
std::vector<std::unique_ptr<DebugLocWriter>> LocListWritersByCU;
LocWriters LocListWritersByCU;
using DebugAbbrevDWOPatchers =
std::unordered_map<uint64_t, std::unique_ptr<DebugAbbrevPatcher>>;
/// Binary patchers for DWO Abbrev sections.
DebugAbbrevDWOPatchers BinaryDWOAbbrevPatchers;
using DebugInfoDWOPatchers =
std::unordered_map<uint64_t, std::unique_ptr<SimpleBinaryPatcher>>;
/// Binary patchers for DWO debug_info sections.
DebugInfoDWOPatchers BinaryDWODebugInfoPatchers;
struct LocListDebugInfoPatchType {
uint64_t DebugInfoOffset;
size_t CUIndex;
uint64_t CUWriterOffset;
};
using VectorLocListDebugInfoPatchType =
std::vector<LocListDebugInfoPatchType>;
/// The list of debug info patches to be made once individual
/// location list writers have been filled
std::vector<LocListDebugInfoPatchType> LocListDebugInfoPatches;
VectorLocListDebugInfoPatchType LocListDebugInfoPatches;
std::unordered_map<uint64_t, VectorLocListDebugInfoPatchType>
DwoLocListDebugInfoPatches;
std::mutex LocListDebugInfoPatchesMutex;
/// Update debug info for all DIEs in \p Unit.
void updateUnitDebugInfo(size_t CUIndex, DWARFUnit *Unit);
void updateUnitDebugInfo(uint64_t CUIndex, DWARFUnit &Unit,
SimpleBinaryPatcher &DebugInfoPatcher,
DebugAbbrevPatcher &AbbrevPatcher);
/// Patches the binary for an object's address ranges to be updated.
/// The object can be a anything that has associated address ranges via either
@@ -70,12 +96,15 @@ class DWARFRewriter {
/// \p Unit Compile unit the object belongs to.
/// \p DIE is the object's DIE in the input binary.
void updateDWARFObjectAddressRanges(const DWARFDie DIE,
uint64_t DebugRangesOffset);
uint64_t DebugRangesOffset,
SimpleBinaryPatcher &DebugInfoPatcher,
DebugAbbrevPatcher &AbbrevPatcher);
std::unique_ptr<LocBufferVector> makeFinalLocListsSection();
std::unique_ptr<LocBufferVector>
makeFinalLocListsSection(SimpleBinaryPatcher &DebugInfoPatcher);
/// Generate new contents for .debug_ranges and .debug_aranges section.
void finalizeDebugSections();
void finalizeDebugSections(SimpleBinaryPatcher &DebugInfoPatcher);
/// Patches the binary for DWARF address ranges (e.g. in functions and lexical
/// blocks) to be updated.
@@ -84,6 +113,8 @@ class DWARFRewriter {
/// Rewrite .gdb_index section if present.
void updateGdbIndexSection();
/// Output .dwo files.
void writeOutDWOFiles(std::unordered_map<uint64_t, std::string> &DWOIdToName);
/// Abbreviations that were converted to use DW_AT_ranges.
std::set<const DWARFAbbreviationDeclaration *> ConvertedRangesAbbrevs;
@@ -119,22 +150,52 @@ class DWARFRewriter {
/// Convert \p Abbrev from using a simple DW_AT_(low|high)_pc range to
/// DW_AT_ranges.
void convertToRanges(const DWARFAbbreviationDeclaration *Abbrev);
void convertToRanges(const DWARFAbbreviationDeclaration *Abbrev,
DebugAbbrevPatcher &AbbrevPatcher);
/// Update \p DIE that was using DW_AT_(low|high)_pc with DW_AT_ranges offset.
void convertToRanges(DWARFDie DIE, uint64_t RangesSectionOffset);
void convertToRanges(DWARFDie DIE, uint64_t RangesSectionOffset,
SimpleBinaryPatcher &DebugInfoPatcher);
/// Same as above, but takes a vector of \p Ranges as a parameter.
void convertToRanges(DWARFDie DIE, const DebugAddressRangesVector &Ranges);
void convertToRanges(DWARFDie DIE, const DebugAddressRangesVector &Ranges,
SimpleBinaryPatcher &DebugInfoPatcher);
/// Patch DW_AT_(low|high)_pc values for the \p DIE based on \p Range.
void patchLowHigh(DWARFDie DIE, DebugAddressRange Range);
void patchLowHigh(DWARFDie DIE, DebugAddressRange Range,
SimpleBinaryPatcher &DebugInfoPatcher);
/// Convert pending ranges associated with the given \p Abbrev.
void convertPending(const DWARFAbbreviationDeclaration *Abbrev);
void convertPending(const DWARFAbbreviationDeclaration *Abbrev,
SimpleBinaryPatcher &DebugInfoPatcher,
DebugAbbrevPatcher &AbbrevPatcher);
/// Adds to Pending Ranges.
/// For Debug Fission also adding to .debug_addr to take care of a case where
/// some entries are not converted to ranges and left as
/// DW_AT_low_pc/DW_AT_high_pc.
void addToPendingRanges(const DWARFAbbreviationDeclaration *Abbrev,
DWARFDie DIE, DebugAddressRangesVector &Ranges,
Optional<uint64_t> DWOId);
/// Once all DIEs were seen, update DW_AT_(low|high)_pc values.
void flushPendingRanges();
void flushPendingRanges(SimpleBinaryPatcher &DebugInfoPatcher);
/// Helper function for creating and returnning DWO DebugInfo and Abbrev
/// patchers.
template <class T, class Patcher>
Patcher *getBinaryDWOPatcherHelper(T &BinaryPatchers, uint64_t DwoId) {
auto Iter = BinaryPatchers.find(DwoId);
if (Iter == BinaryPatchers.end()) {
// Using make_pair instead of {} to work around bug in older version of
// the library. https://timsong-cpp.github.io/lwg-issues/2354
Iter = BinaryPatchers
.insert(std::make_pair(DwoId, std::make_unique<Patcher>()))
.first;
}
return static_cast<Patcher *>(Iter->second.get());
}
public:
DWARFRewriter(BinaryContext &BC) : BC(BC) {}
@@ -145,6 +206,21 @@ public:
/// Computes output .debug_line line table offsets for each compile unit,
/// and updates stmt_list for a corresponding compile unit.
void updateLineTableOffsets();
/// Returns a DWO Debug Info Patcher for DWO ID.
/// Creates a new instance if it does not already exist.
SimpleBinaryPatcher *getBinaryDWODebugInfoPatcher(uint64_t DwoId) {
return getBinaryDWOPatcherHelper<DebugInfoDWOPatchers, SimpleBinaryPatcher>(
BinaryDWODebugInfoPatchers, DwoId);
}
/// Returns a DWO Abbrev Patcher for DWO ID.
/// Creates a new instance if it's not already exists.
DebugAbbrevPatcher *getBinaryDWOAbbrevPatcher(uint64_t DwoId) {
return getBinaryDWOPatcherHelper<DebugAbbrevDWOPatchers,
DebugAbbrevPatcher>(
BinaryDWOAbbrevPatchers, DwoId);
}
};
} // namespace bolt

View File

@@ -9,11 +9,17 @@
//===----------------------------------------------------------------------===//
#include "DebugData.h"
#include "BinaryBasicBlock.h"
#include "BinaryFunction.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <limits>
#undef DEBUG_TYPE
#define DEBUG_TYPE "bolt-debug-info"
@@ -52,7 +58,7 @@ uint64_t writeAddressRanges(
} // namespace
DebugRangesSectionWriter::DebugRangesSectionWriter(BinaryContext *BC) {
DebugRangesSectionWriter::DebugRangesSectionWriter() {
RangesBuffer = std::make_unique<RangesBufferVector>();
RangesStream = std::make_unique<raw_svector_ostream>(*RangesBuffer);
@@ -91,6 +97,11 @@ DebugRangesSectionWriter::addRanges(const DebugAddressRangesVector &Ranges) {
return EntryOffset;
}
uint64_t DebugRangesSectionWriter::getSectionOffset() {
std::lock_guard<std::mutex> Lock(WriterMutex);
return SectionOffset;
}
void DebugARangesSectionWriter::addCURanges(uint64_t CUOffset,
DebugAddressRangesVector &&Ranges) {
std::lock_guard<std::mutex> Lock(CUAddressRangesMutex);
@@ -141,6 +152,109 @@ void DebugARangesSectionWriter::writeARangesSection(
}
}
DebugAddrWriter::DebugAddrWriter(BinaryContext *Bc) { BC = Bc; }
void DebugAddrWriter::AddressForDWOCU::dump() {
std::vector<IndexAddressPair> SortedMap(indexToAddressBegin(),
indexToAdddessEnd());
// Sorting address in increasing order of indices.
std::sort(SortedMap.begin(), SortedMap.end(),
[](const IndexAddressPair &A, const IndexAddressPair &B) {
return A.first < B.first;
});
for (auto &Pair : SortedMap)
dbgs() << Twine::utohexstr(Pair.second) << "\t" << Pair.first << "\n";
}
uint32_t DebugAddrWriter::getIndexFromAddress(uint64_t Address,
uint64_t DWOId) {
if (!AddressMaps.count(DWOId))
AddressMaps[DWOId] = AddressForDWOCU();
AddressForDWOCU &Map = AddressMaps[DWOId];
auto Entry = Map.find(Address);
if (Entry == Map.end()) {
auto Index = Map.getNextIndex();
Entry = Map.insert(Address, Index).first;
}
return Entry->second;
}
// Case1) Address is not in map insert in to AddresToIndex and IndexToAddres
// Case2) Address is in the map but Index is higher or equal. Need to update
// IndexToAddrss. Case3) Address is in the map but Index is lower. Need to
// update AddressToIndex and IndexToAddress
void DebugAddrWriter::addIndexAddress(uint64_t Address, uint32_t Index,
uint64_t DWOId) {
AddressForDWOCU &Map = AddressMaps[DWOId];
auto Entry = Map.find(Address);
if (Entry != Map.end()) {
if (Entry->second > Index)
Map.updateAddressToIndex(Address, Index);
Map.updateIndexToAddrss(Address, Index);
} else
Map.insert(Address, Index);
}
AddressSectionBuffer DebugAddrWriter::finalize() {
// Need to layout all sections within .debug_addr
// Within each section sort Address by index.
AddressSectionBuffer Buffer;
raw_svector_ostream AddressStream(Buffer);
for (std::unique_ptr<DWARFUnit> &CU : BC->DwCtx->compile_units()) {
Optional<uint64_t> DWOId = CU->getDWOId();
// Handling the case wehre debug information is a mix of Debug fission and
// monolitic.
if (!DWOId)
continue;
auto AM = AddressMaps.find(*DWOId);
// Adding to map even if it did not contribute to .debug_addr.
// The Skeleton CU will still have DW_AT_GNU_addr_base.
DWOIdToOffsetMap[*DWOId] = Buffer.size();
// If does not exist this CUs DWO section didn't contribute to .debug_addr.
if (AM == AddressMaps.end())
continue;
std::vector<IndexAddressPair> SortedMap(AM->second.indexToAddressBegin(),
AM->second.indexToAdddessEnd());
// Sorting address in increasing order of indices.
std::sort(SortedMap.begin(), SortedMap.end(),
[](const IndexAddressPair &A, const IndexAddressPair &B) {
return A.first < B.first;
});
uint8_t AddrSize = CU->getAddressByteSize();
uint32_t Counter = 0;
auto WriteAddress = [&](uint64_t Address) -> void {
++Counter;
switch (AddrSize) {
default:
assert(false && "Address Size is invalid.");
case 4:
support::endian::write(AddressStream, static_cast<uint32_t>(Address),
support::little);
break;
case 8:
support::endian::write(AddressStream, Address, support::little);
break;
}
};
for (const IndexAddressPair &Val : SortedMap) {
while (Val.first > Counter)
WriteAddress(0);
WriteAddress(Val.second);
}
}
return Buffer;
}
uint64_t DebugAddrWriter::getOffset(uint64_t DWOId) {
auto Iter = DWOIdToOffsetMap.find(DWOId);
assert(Iter != DWOIdToOffsetMap.end() &&
"Offset in to.debug_addr was not found for DWO ID.");
return Iter->second;
}
DebugLocWriter::DebugLocWriter(BinaryContext *BC) {
LocBuffer = std::make_unique<LocBufferVector>();
LocStream = std::make_unique<raw_svector_ostream>(*LocBuffer);
@@ -173,6 +287,55 @@ DebugLocWriter::addList(const DebugLocationsVector &LocList) {
return EntryOffset;
}
DebugAddrWriter *DebugLoclistWriter::AddrWriter = nullptr;
void DebugLoclistWriter::finalizePatches() {
auto numOfBytes = [](uint32_t Val) -> uint32_t {
int LogVal = (int)std::log2(Val) + 1;
uint32_t CeilVal = (LogVal + 8 - 1) / 8;
return !Val ? 1 : CeilVal;
};
(void)numOfBytes;
for (const auto &Patch : IndexPatches) {
uint32_t Index = AddrWriter->getIndexFromAddress(Patch.Address, DWOId);
assert(numOfBytes(Index) <= DebugLoclistWriter::NumBytesForIndex &&
"Index size in DebugLocation too large.");
std::string Buff;
raw_string_ostream OS(Buff);
encodeULEB128(Index, OS, DebugLoclistWriter::NumBytesForIndex);
for (uint32_t I = 0; I < DebugLoclistWriter::NumBytesForIndex; ++I) {
(*LocBuffer)[Patch.Offset + I] = Buff[I];
}
}
}
uint64_t DebugLoclistWriter::addList(const DebugLocationsVector &LocList) {
if (LocList.empty())
return EmptyListTag;
uint64_t EntryOffset = LocBuffer->size();
for (const DebugLocationEntry &Entry : LocList) {
support::endian::write(*LocStream,
static_cast<uint8_t>(dwarf::DW_LLE_startx_length),
support::little);
IndexPatches.emplace_back(static_cast<uint32_t>(LocBuffer->size()),
Entry.LowPC);
LocStream->write_zeros(DebugLoclistWriter::NumBytesForIndex);
// TODO: Support DWARF5
support::endian::write(*LocStream,
static_cast<uint32_t>(Entry.HighPC - Entry.LowPC),
support::little);
support::endian::write(*LocStream, static_cast<uint16_t>(Entry.Expr.size()),
support::little);
*LocStream << StringRef(reinterpret_cast<const char *>(Entry.Expr.data()),
Entry.Expr.size());
}
support::endian::write(*LocStream,
static_cast<uint8_t>(dwarf::DW_LLE_end_of_list),
support::little);
return EntryOffset;
}
void SimpleBinaryPatcher::addBinaryPatch(uint32_t Offset,
const std::string &NewValue) {
Patches.emplace_back(Offset, NewValue);
@@ -221,13 +384,16 @@ void SimpleBinaryPatcher::patchBinary(std::string &BinaryContents) {
}
void DebugAbbrevPatcher::addAttributePatch(
const DWARFAbbreviationDeclaration *Abbrev,
dwarf::Attribute AttrTag,
uint8_t NewAttrTag,
uint8_t NewAttrForm) {
const DWARFAbbreviationDeclaration *Abbrev, dwarf::Attribute AttrTag,
dwarf::Attribute NewAttrTag, uint8_t NewAttrForm) {
assert(Abbrev && "no abbreviation specified");
AbbrevPatches.emplace(
AbbrevAttrPatch{Abbrev, AttrTag, NewAttrTag, NewAttrForm});
if (std::numeric_limits<uint8_t>::max() >= NewAttrTag)
AbbrevPatches.emplace(
AbbrevAttrPatch{Abbrev, AttrTag, NewAttrTag, NewAttrForm});
else
AbbrevNonStandardPatches.emplace_back(
AbbrevAttrPatch{Abbrev, AttrTag, NewAttrTag, NewAttrForm});
}
void DebugAbbrevPatcher::patchBinary(std::string &Contents) {
@@ -238,17 +404,67 @@ void DebugAbbrevPatcher::patchBinary(std::string &Contents) {
Patch.Abbrev->findAttribute(Patch.Attr);
assert(Attribute && "Specified attribute doesn't occur in abbreviation.");
// Because we're only handling standard values (i.e. no DW_FORM_GNU_* or
// DW_AT_APPLE_*), they are all small (< 128) and encoded in a single
// byte in ULEB128, otherwise it'll be more tricky as we may need to
// grow or shrink the section.
Patcher.addBytePatch(Attribute->AttrOffset, Patch.NewAttr);
Patcher.addBytePatch(Attribute->AttrOffset,
static_cast<uint8_t>(Patch.NewAttr));
Patcher.addBytePatch(Attribute->FormOffset, Patch.NewForm);
}
Patcher.patchBinary(Contents);
if (AbbrevNonStandardPatches.empty())
return;
std::string Section;
Section.reserve(Contents.size() + AbbrevNonStandardPatches.size() / 2);
const char *Ptr = Contents.c_str();
uint32_t Start = 0;
std::string Buff;
auto Encode = [&](uint16_t Value) -> std::string {
Buff.clear();
raw_string_ostream OS(Buff);
encodeULEB128(Value, OS, 2);
return Buff;
};
for (const AbbrevAttrPatch &Patch : AbbrevNonStandardPatches) {
const DWARFAbbreviationDeclaration::AttributeSpec *const Attribute =
Patch.Abbrev->findAttribute(Patch.Attr);
assert(Attribute && "Specified attribute doesn't occur in abbreviation.");
assert(Start <= Attribute->AttrOffset &&
"Offsets are not in sequential order.");
// Assuming old attribute is 1 byte. Otherwise will need to add logic to
// determine it's size at runtime.
assert(std::numeric_limits<uint8_t>::max() >= Patch.Attr &&
"Old attribute is greater then 1 byte.");
Section.append(Ptr + Start, Attribute->AttrOffset - Start);
Section.append(Encode(Patch.NewAttr));
Section.append(std::string(1, Patch.NewForm));
Start = Attribute->FormOffset + 1;
}
Section.append(Ptr + Start, Contents.size() - Start);
Contents = std::move(Section);
}
void DebugStrWriter::create() {
StrBuffer = std::make_unique<DebugStrBufferVector>();
StrStream = std::make_unique<raw_svector_ostream>(*StrBuffer);
}
void DebugStrWriter::initialize() {
auto StrSection = BC->DwCtx->getDWARFObj().getStrSection();
(*StrStream) << StrSection;
}
uint32_t DebugStrWriter::addString(StringRef Str) {
if (StrBuffer->empty())
initialize();
auto Offset = StrBuffer->size();
(*StrStream) << Str;
StrStream->write_zeros(1);
return Offset;
}
} // namespace bolt
} // namespace llvm

View File

@@ -17,9 +17,11 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdint>
#include <map>
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
@@ -103,7 +105,7 @@ using RangesBufferVector = SmallVector<char, 16>;
/// Serializes the .debug_ranges DWARF section.
class DebugRangesSectionWriter {
public:
DebugRangesSectionWriter(BinaryContext *BC);
DebugRangesSectionWriter();
/// Add ranges with caching.
uint64_t
@@ -117,6 +119,9 @@ public:
/// to .debug_ranges
uint64_t getEmptyRangesOffset() const { return EmptyRangesOffset; }
/// Returns the SectionOffset.
uint64_t getSectionOffset();
std::unique_ptr<RangesBufferVector> finalize() {
return std::move(RangesBuffer);
}
@@ -167,16 +172,125 @@ private:
std::mutex CUAddressRangesMutex;
};
using IndexAddressPair = std::pair<uint32_t, uint64_t>;
using AddressToIndexMap = std::unordered_map<uint64_t, uint32_t>;
using IndexToAddressMap = std::unordered_map<uint32_t, uint64_t>;
using AddressSectionBuffer = SmallVector<char, 4>;
class DebugAddrWriter {
public:
DebugAddrWriter() = delete;
DebugAddrWriter(BinaryContext *BC_);
/// Given an address returns an index in .debug_addr.
/// Adds Address to map.
uint32_t getIndexFromAddress(uint64_t Address, uint64_t DWOId);
/// Adds {Address, Index} to DWO ID CU.
void addIndexAddress(uint64_t Address, uint32_t Index, uint64_t DWOId);
/// Creates consolidated .debug_addr section, and builds DWOID to offset map.
AddressSectionBuffer finalize();
/// Given DWOID returns offset of this CU in to .debug_addr section.
uint64_t getOffset(uint64_t DWOId);
/// Returns False if .debug_addr section was created..
bool isInitialized() const { return !AddressMaps.empty(); }
private:
class AddressForDWOCU {
public:
AddressToIndexMap::iterator find(uint64_t Adddress) {
return AddressToIndex.find(Adddress);
}
AddressToIndexMap::iterator end() { return AddressToIndex.end(); }
AddressToIndexMap::iterator begin() { return AddressToIndex.begin(); }
IndexToAddressMap::iterator indexToAdddessEnd() {
return IndexToAddress.end();
}
IndexToAddressMap::iterator indexToAddressBegin() {
return IndexToAddress.begin();
}
uint32_t getNextIndex() {
while (IndexToAddress.count(CurrentIndex))
++CurrentIndex;
return CurrentIndex;
}
/// Inserts elements in to IndexToAddress and AddressToIndex.
/// Follows the same semantics as unordered_map insert.
std::pair<AddressToIndexMap::iterator, bool> insert(uint64_t Address,
uint32_t Index) {
IndexToAddress.insert({Index, Address});
return AddressToIndex.insert({Address, Index});
}
/// Updates AddressToIndex Map.
/// Follows the same symantics as unordered map [].
void updateAddressToIndex(uint64_t Address, uint32_t Index) {
AddressToIndex[Address] = Index;
}
/// Updates IndexToAddress Map.
/// Follows the same symantics as unordered map [].
void updateIndexToAddrss(uint64_t Address, uint32_t Index) {
IndexToAddress[Index] = Address;
}
void dump();
private:
AddressToIndexMap AddressToIndex;
IndexToAddressMap IndexToAddress;
uint32_t CurrentIndex{0};
};
BinaryContext *BC;
/// Maps DWOID to AddressForDWOCU.
std::unordered_map<uint64_t, AddressForDWOCU> AddressMaps;
/// Maps DWOID to offset within .debug_addr section.
std::unordered_map<uint64_t, uint64_t> DWOIdToOffsetMap;
};
using DebugStrBufferVector = SmallVector<char, 16>;
class DebugStrWriter {
public:
DebugStrWriter() = delete;
DebugStrWriter(BinaryContext *Bc) : BC(Bc) { create(); }
std::unique_ptr<DebugStrBufferVector> finalize() {
return std::move(StrBuffer);
}
/// Adds string to .debug_str.
/// On first invokation it initializes internal data stractures.
uint32_t addString(StringRef Str);
/// Returns False if no strings were added to .debug_str.
bool isInitialized() const { return !StrBuffer->empty(); }
private:
/// Initializes Buffer and Stream.
void initialize();
/// Creats internal data stractures.
void create();
std::unique_ptr<DebugStrBufferVector> StrBuffer;
std::unique_ptr<raw_svector_ostream> StrStream;
BinaryContext *BC;
};
using LocBufferVector = SmallVector<char, 16>;
enum class LocWriterKind { DebugLocWriter, DebugLoclistWriter };
/// Serializes part of a .debug_loc DWARF section with LocationLists.
class DebugLocWriter {
public:
DebugLocWriter() = delete;
DebugLocWriter(BinaryContext *BC);
virtual ~DebugLocWriter(){};
uint64_t addList(const DebugLocationsVector &LocList);
virtual uint64_t addList(const DebugLocationsVector &LocList);
std::unique_ptr<LocBufferVector> finalize() {
virtual std::unique_ptr<LocBufferVector> finalize() {
return std::move(LocBuffer);
}
@@ -188,16 +302,61 @@ public:
/// be stored while we use max 64 bit value as empty tag
static constexpr uint64_t EmptyListTag = -1;
private:
LocWriterKind getKind() const { return Kind; }
static bool classof(const DebugLocWriter *Writer) {
return Writer->getKind() == LocWriterKind::DebugLocWriter;
}
protected:
std::unique_ptr<LocBufferVector> LocBuffer;
std::unique_ptr<raw_svector_ostream> LocStream;
/// Current offset in the section (updated as new entries are written).
/// Starts with 0 here since this only writes part of a full location lists
/// section. In the final section, the first 16 bytes are reserved for an
/// empty list.
uint32_t SectionOffset{0};
LocWriterKind Kind{LocWriterKind::DebugLocWriter};
};
class DebugLoclistWriter : public DebugLocWriter {
public:
~DebugLoclistWriter() {}
DebugLoclistWriter() = delete;
DebugLoclistWriter(BinaryContext *BC, uint64_t DWOId_)
: DebugLocWriter(BC), DWOId(DWOId_) {
Kind = LocWriterKind::DebugLoclistWriter;
assert(DebugLoclistWriter::AddrWriter &&
"Please use SetAddressWriter to initialize "
"DebugAddrWriter before instantiation.");
}
static void setAddressWriter(DebugAddrWriter *AddrW) { AddrWriter = AddrW; }
/// Writes out locationList, with index in to .debug_addr to be patched later.
virtual uint64_t addList(const DebugLocationsVector &LocList);
/// Finalizes all the location by patching correct index in to .debug_addr.
void finalizePatches();
static bool classof(const DebugLocWriter *Writer) {
return Writer->getKind() == LocWriterKind::DebugLoclistWriter;
}
private:
class Patch {
public:
Patch() = delete;
Patch(uint64_t O, uint64_t A) : Offset(O), Address(A) {}
uint64_t Offset{0};
uint64_t Address{0};
};
static DebugAddrWriter *AddrWriter;
uint64_t DWOId{0};
std::unordered_map<uint64_t, uint32_t> AddressMap;
std::vector<Patch> IndexPatches;
constexpr static uint32_t NumBytesForIndex{3};
};
/// Abstract interface for classes that apply modifications to a binary string.
@@ -219,8 +378,15 @@ private:
/// being written at the offset \p Offset .
void addLEPatch(uint32_t Offset, uint64_t NewValue, size_t ByteSize);
/// RangeBase for DWO DebugInfo Patcher.
uint64_t RangeBase{0};
/// Gets reset to false when setRangeBase is invoked.
/// Gets set to true when getRangeBase is called
uint64_t WasRangeBaseUsed{false};
public:
~SimpleBinaryPatcher() {}
virtual ~SimpleBinaryPatcher() {}
/// Adds a patch to replace the contents of the binary string starting at the
/// specified \p Offset with the string \p NewValue.
@@ -243,17 +409,32 @@ public:
/// needed to encode \p Value.
void addUDataPatch(uint32_t Offset, uint64_t Value, uint64_t Size);
void patchBinary(std::string &BinaryContents) override;
/// Setting DW_AT_GNU_ranges_base
void setRangeBase(uint64_t Rb) {
WasRangeBaseUsed = false;
RangeBase = Rb;
}
/// Gets DW_AT_GNU_ranges_base
uint64_t getRangeBase() {
WasRangeBaseUsed = true;
return RangeBase;
}
/// Proxy for if we broke up low_pc/high_pc to ranges.
bool getWasRangBasedUsed() const { return WasRangeBaseUsed; }
virtual void patchBinary(std::string &BinaryContents) override;
};
/// Apply small modifications to the .debug_abbrev DWARF section.
class DebugAbbrevPatcher : public BinaryPatcher {
private:
public:
/// Patch of changing one attribute to another.
struct AbbrevAttrPatch {
const DWARFAbbreviationDeclaration *Abbrev;
dwarf::Attribute Attr; // ID of attribute to be replaced.
uint8_t NewAttr; // ID of the new attribute.
dwarf::Attribute NewAttr; // ID of the new attribute.
uint8_t NewForm; // Form of the new attribute.
bool operator==(const AbbrevAttrPatch &RHS) const {
@@ -267,22 +448,39 @@ private:
}
};
private:
std::unordered_set<AbbrevAttrPatch, AbbrevHash> AbbrevPatches;
/// Using Vector because this is currently being used for specific purpose of
/// patching DW_AT_GNU_ranges_base. So don't need fast look up, but do need
/// them to be in order.
std::vector<AbbrevAttrPatch> AbbrevNonStandardPatches;
public:
~DebugAbbrevPatcher() { }
virtual ~DebugAbbrevPatcher() {}
/// Adds a patch to change an attribute of the abbreviation
/// \p Abbrev the abbreviation to be modified.
/// \p AttrTag ID of the attribute to be replaced.
/// \p NewAttrTag ID of the new attribute.
/// \p NewAttrForm Form of the new attribute.
/// We only handle standard forms, that are encoded in a single byte.
/// If None standard form is added through this API it will expand the section
/// to accomidate it.
void addAttributePatch(const DWARFAbbreviationDeclaration *Abbrev,
dwarf::Attribute AttrTag,
uint8_t NewAttrTag,
dwarf::Attribute AttrTag, dwarf::Attribute NewAttrTag,
uint8_t NewAttrForm);
void patchBinary(std::string &Contents) override;
virtual void patchBinary(std::string &Contents) override;
/// Finds an abbreviation patch.
/// \p AbbrevDecl the abbreviation declaration.
/// \p AttrTag Attribute to search for.
/// Returns nullptr if patch doesn't exist.
const AbbrevAttrPatch *find(const DWARFAbbreviationDeclaration *AbbrevDecl,
dwarf::Attribute AttrTag) {
auto Iter = AbbrevPatches.find({AbbrevDecl, AttrTag, AttrTag, 0});
if (Iter == AbbrevPatches.end())
return nullptr;
return &(*Iter);
}
};
} // namespace bolt

View File

@@ -438,18 +438,8 @@ void MachORewriteInstance::emitAndLink() {
std::unique_ptr<buffer_ostream> BOS =
std::make_unique<buffer_ostream>(TempOut->os());
raw_pwrite_stream *OS = BOS.get();
auto Streamer = BC->createStreamer(*OS);
MCCodeEmitter *MCE =
BC->TheTarget->createMCCodeEmitter(*BC->MII, *BC->MRI, *BC->Ctx);
MCAsmBackend *MAB =
BC->TheTarget->createMCAsmBackend(*BC->STI, *BC->MRI, MCTargetOptions());
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(*OS);
std::unique_ptr<MCStreamer> Streamer(BC->TheTarget->createMCObjectStreamer(
*BC->TheTriple, *BC->Ctx, std::unique_ptr<MCAsmBackend>(MAB),
std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *BC->STI,
/* RelaxAll */ false,
/* IncrementalLinkerCompatible */ false,
/* DWARFMustBeAtTheEnd */ false));
emitBinaryContext(*Streamer, *BC, getOrgSecPrefix());
Streamer->Finish();

View File

@@ -419,7 +419,9 @@ bool processAllFunctions() {
} // namespace opts
constexpr const char *RewriteInstance::SectionsToOverwrite[];
constexpr const char *RewriteInstance::DebugSectionsToOverwrite[];
std::vector<std::string> RewriteInstance::DebugSectionsToOverwrite = {
".debug_aranges", ".debug_line", ".debug_loc",
".debug_ranges", ".gdb_index", ".debug_addr"};
const char RewriteInstance::TimerGroupName[] = "rewrite";
const char RewriteInstance::TimerGroupDesc[] = "Rewrite passes";
@@ -2996,17 +2998,7 @@ void RewriteInstance::emitAndLink() {
// Implicitly MCObjectStreamer takes ownership of MCAsmBackend (MAB)
// and MCCodeEmitter (MCE). ~MCObjectStreamer() will delete these
// two instances.
MCCodeEmitter *MCE =
BC->TheTarget->createMCCodeEmitter(*BC->MII, *BC->MRI, *BC->Ctx);
MCAsmBackend *MAB =
BC->TheTarget->createMCAsmBackend(*BC->STI, *BC->MRI, MCTargetOptions());
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(*OS);
std::unique_ptr<MCStreamer> Streamer(BC->TheTarget->createMCObjectStreamer(
*BC->TheTriple, *BC->Ctx, std::unique_ptr<MCAsmBackend>(MAB),
std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *BC->STI,
/* RelaxAll */ false,
/* IncrementalLinkerCompatible */ false,
/* DWARFMustBeAtTheEnd */ false));
std::unique_ptr<MCStreamer> Streamer = BC->createStreamer(*OS);
if (EHFrameSection) {
if (opts::UseOldText || opts::StrictMode) {
@@ -3698,7 +3690,8 @@ void RewriteInstance::rewriteNoteSections() {
// New section size.
uint64_t Size = 0;
bool DataWritten = false;
uint8_t *SectionData = nullptr;
// Copy over section contents unless it's one of the sections we overwrite.
if (!willOverwriteSection(SectionName)) {
Size = Section.sh_size;
@@ -3706,20 +3699,30 @@ void RewriteInstance::rewriteNoteSections() {
std::string(InputFile->getData().substr(Section.sh_offset, Size));
if (BSec && BSec->getPatcher())
BSec->getPatcher()->patchBinary(Data);
OS << Data;
// Add padding as the section extension might rely on the alignment.
Size = appendPadding(OS, Size, Section.sh_addralign);
// Section was expanded, so need to treat it as overwrite.
if (Size != Data.size()) {
BSec = BC->registerOrUpdateNoteSection(SectionName, copyByteArray(Data),
Data.size());
Size = 0;
} else {
OS << Data;
DataWritten = true;
// Add padding as the section extension might rely on the alignment.
Size = appendPadding(OS, Size, Section.sh_addralign);
}
}
// Perform section post-processing.
uint8_t *SectionData = nullptr;
if (BSec && !BSec->isAllocatable()) {
assert(BSec->getAlignment() <= Section.sh_addralign &&
"alignment exceeds value in file");
if (BSec->getAllocAddress()) {
assert(!DataWritten && "Writing section twice.");
SectionData = BSec->getOutputData();
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: " << (Size ? "appending" : "writing")
<< " contents to section " << SectionName << '\n');
OS.write(reinterpret_cast<char *>(SectionData),
@@ -4779,18 +4782,9 @@ void RewriteInstance::rewriteFile() {
// We obtain an asm-specific writer so that we can emit nops in an
// architecture-specific way at the end of the function.
MCCodeEmitter *MCE =
BC->TheTarget->createMCCodeEmitter(*BC->MII, *BC->MRI, *BC->Ctx);
MCAsmBackend *MAB =
BC->TheTarget->createMCAsmBackend(*BC->STI, *BC->MRI, MCTargetOptions());
std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(OS);
std::unique_ptr<MCStreamer> Streamer(BC->TheTarget->createMCObjectStreamer(
*BC->TheTriple, *BC->Ctx, std::unique_ptr<MCAsmBackend>(MAB),
std::move(OW), std::unique_ptr<MCCodeEmitter>(MCE), *BC->STI,
/* RelaxAll */ false,
/*IncrementalLinkerCompatible */ false,
/* DWARFMustBeAtTheEnd */ false));
std::unique_ptr<MCAsmBackend> MAB(
BC->TheTarget->createMCAsmBackend(*BC->STI, *BC->MRI, MCTargetOptions()));
auto Streamer = BC->createStreamer(OS);
// Make sure output stream has enough reserved space, otherwise
// pwrite() will fail.
uint64_t Offset = OS.seek(getFileOffsetForAddress(NextAvailableAddress));
@@ -5085,7 +5079,7 @@ bool RewriteInstance::willOverwriteSection(StringRef SectionName) {
if (SectionName == OverwriteName)
return true;
}
for (const char *const &OverwriteName : DebugSectionsToOverwrite) {
for (std::string &OverwriteName : DebugSectionsToOverwrite) {
if (SectionName == OverwriteName)
return true;
}

View File

@@ -343,13 +343,7 @@ public:
};
/// Debug section to we overwrite while updating the debug info.
static constexpr const char *DebugSectionsToOverwrite[] = {
".debug_aranges",
".debug_line",
".debug_loc",
".debug_ranges",
".gdb_index",
};
static std::vector<std::string> DebugSectionsToOverwrite;
/// Return true if the section holds debug information.
static bool isDebugSection(StringRef SectionName);
@@ -357,6 +351,11 @@ public:
/// Return true if the section holds linux kernel symbol information.
static bool isKSymtabSection(StringRef SectionName);
/// Adds Debug section to overwrite.
static void addToDebugSectionsToOverwrite(const char *Section) {
DebugSectionsToOverwrite.emplace_back(Section);
}
private:
/// Get the contents of the LSDA section for this binary.
ArrayRef<uint8_t> getLSDAData();

View File

@@ -0,0 +1,7 @@
SECTIONS
{
. = 0x4002a0;
.text : { *(.text*) }
. = 0x601000;
.data : { *(.data) }
}

View File

@@ -0,0 +1,446 @@
.text
.file "debug-fission-simple.cpp"
.file 1 "" "debug-fission-simple.cpp"
.section .text._Z7doStuffi,"ax",@progbits
.globl _Z7doStuffi # -- Begin function _Z7doStuffi
.p2align 4, 0x90
.type _Z7doStuffi,@function
_Z7doStuffi: # @_Z7doStuffi
.Lfunc_begin0:
.loc 1 3 0 # debug-fission-simple.cpp:3:0
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -4(%rbp)
.Ltmp0:
.loc 1 4 11 prologue_end # debug-fission-simple.cpp:4:11
cmpl $5, -4(%rbp)
.Ltmp1:
.loc 1 4 7 is_stmt 0 # debug-fission-simple.cpp:4:7
jne .LBB0_2
# %bb.1: # %if.then
.Ltmp2:
.loc 1 5 16 is_stmt 1 # debug-fission-simple.cpp:5:16
movl _ZL3foo, %eax
.loc 1 5 14 is_stmt 0 # debug-fission-simple.cpp:5:14
addl $1, %eax
.loc 1 5 9 # debug-fission-simple.cpp:5:9
addl -4(%rbp), %eax
movl %eax, -4(%rbp)
.loc 1 5 5 # debug-fission-simple.cpp:5:5
jmp .LBB0_3
.LBB0_2: # %if.else
.loc 1 7 9 is_stmt 1 # debug-fission-simple.cpp:7:9
movl -4(%rbp), %eax
subl $1, %eax
movl %eax, -4(%rbp)
.Ltmp3:
.LBB0_3: # %if.end
.loc 1 8 10 # debug-fission-simple.cpp:8:10
movl -4(%rbp), %eax
.loc 1 8 3 is_stmt 0 # debug-fission-simple.cpp:8:3
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Ltmp4:
.Lfunc_end0:
.size _Z7doStuffi, .Lfunc_end0-_Z7doStuffi
.cfi_endproc
# -- End function
.section .text._Z8doStuff2i,"ax",@progbits
.globl _Z8doStuff2i # -- Begin function _Z8doStuff2i
.p2align 4, 0x90
.type _Z8doStuff2i,@function
_Z8doStuff2i: # @_Z8doStuff2i
.Lfunc_begin1:
.loc 1 11 0 is_stmt 1 # debug-fission-simple.cpp:11:0
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -4(%rbp)
.Ltmp5:
.loc 1 12 14 prologue_end # debug-fission-simple.cpp:12:14
movl -4(%rbp), %eax
addl $3, %eax
movl %eax, -4(%rbp)
.loc 1 12 3 is_stmt 0 # debug-fission-simple.cpp:12:3
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Ltmp6:
.Lfunc_end1:
.size _Z8doStuff2i, .Lfunc_end1-_Z8doStuff2i
.cfi_endproc
# -- End function
.section .text._Z6_startv,"ax",@progbits
.globl _Z6_startv # -- Begin function _Z6_startv
.p2align 4, 0x90
.type _Z6_startv,@function
_Z6_startv: # @_Z6_startv
.Lfunc_begin2:
.loc 1 15 0 is_stmt 1 # debug-fission-simple.cpp:15:0
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
.Ltmp7:
.loc 1 16 7 prologue_end # debug-fission-simple.cpp:16:7
movl $4, -4(%rbp)
.loc 1 17 18 # debug-fission-simple.cpp:17:18
movl -4(%rbp), %edi
.loc 1 17 10 is_stmt 0 # debug-fission-simple.cpp:17:10
callq _Z7doStuffi
.loc 1 17 3 # debug-fission-simple.cpp:17:3
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Ltmp8:
.Lfunc_end2:
.size _Z6_startv, .Lfunc_end2-_Z6_startv
.cfi_endproc
# -- End function
.type _ZL3foo,@object # @_ZL3foo
.data
.p2align 2
_ZL3foo:
.long 2 # 0x2
.size _ZL3foo, 4
.section .debug_abbrev,"",@progbits
.byte 1 # Abbreviation Code
.byte 17 # DW_TAG_compile_unit
.byte 0 # DW_CHILDREN_no
.byte 16 # DW_AT_stmt_list
.byte 23 # DW_FORM_sec_offset
.byte 27 # DW_AT_comp_dir
.byte 14 # DW_FORM_strp
.ascii "\264B" # DW_AT_GNU_pubnames
.byte 25 # DW_FORM_flag_present
.ascii "\260B" # DW_AT_GNU_dwo_name
.byte 14 # DW_FORM_strp
.ascii "\261B" # DW_AT_GNU_dwo_id
.byte 7 # DW_FORM_data8
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 85 # DW_AT_ranges
.byte 23 # DW_FORM_sec_offset
.ascii "\263B" # DW_AT_GNU_addr_base
.byte 23 # DW_FORM_sec_offset
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)
.section .debug_info,"",@progbits
.Lcu_begin0:
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
.Ldebug_info_start0:
.short 4 # DWARF version number
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
.byte 1 # Abbrev [1] 0xb:0x25 DW_TAG_compile_unit
.long .Lline_table_start0 # DW_AT_stmt_list
.long .Lskel_string0 # DW_AT_comp_dir
# DW_AT_GNU_pubnames
.long .Lskel_string1 # DW_AT_GNU_dwo_name
.quad 436953012669069206 # DW_AT_GNU_dwo_id
.quad 0 # DW_AT_low_pc
.long .Ldebug_ranges0 # DW_AT_ranges
.long .Laddr_table_base0 # DW_AT_GNU_addr_base
.Ldebug_info_end0:
.section .debug_ranges,"",@progbits
.Ldebug_ranges0:
.quad .Lfunc_begin0
.quad .Lfunc_end0
.quad .Lfunc_begin1
.quad .Lfunc_end1
.quad .Lfunc_begin2
.quad .Lfunc_end2
.quad 0
.quad 0
.section .debug_str,"MS",@progbits,1
.Lskel_string0:
.asciz "" # string offset=0
.Lskel_string1:
.asciz "debug-fission-simple.dwo" # string offset=47
.section .debug_str.dwo,"eMS",@progbits,1
.Linfo_string0:
.asciz "foo" # string offset=0
.Linfo_string1:
.asciz "int" # string offset=4
.Linfo_string2:
.asciz "_ZL3foo" # string offset=8
.Linfo_string3:
.asciz "_Z7doStuffi" # string offset=16
.Linfo_string4:
.asciz "doStuff" # string offset=28
.Linfo_string5:
.asciz "_Z8doStuff2i" # string offset=36
.Linfo_string6:
.asciz "doStuff2" # string offset=49
.Linfo_string7:
.asciz "_Z6_startv" # string offset=58
.Linfo_string8:
.asciz "_start" # string offset=69
.Linfo_string9:
.asciz "val" # string offset=76
.Linfo_string10:
.asciz "clang version 13.0.0" # string offset=80
.Linfo_string11:
.asciz "debug-fission-simple.cpp" # string offset=214
.Linfo_string12:
.asciz "debug-fission-simple.dwo" # string offset=239
.section .debug_str_offsets.dwo,"e",@progbits
.long 0
.long 4
.long 8
.long 16
.long 28
.long 36
.long 49
.long 58
.long 69
.long 76
.long 80
.long 214
.long 239
.section .debug_info.dwo,"e",@progbits
.long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit
.Ldebug_info_dwo_start0:
.short 4 # DWARF version number
.long 0 # Offset Into Abbrev. Section
.byte 8 # Address Size (in bytes)
.byte 1 # Abbrev [1] 0xb:0x73 DW_TAG_compile_unit
.byte 10 # DW_AT_producer
.short 4 # DW_AT_language
.byte 11 # DW_AT_name
.byte 12 # DW_AT_GNU_dwo_name
.quad 436953012669069206 # DW_AT_GNU_dwo_id
.byte 2 # Abbrev [2] 0x19:0xc DW_TAG_variable
.byte 0 # DW_AT_name
.long 37 # DW_AT_type
.byte 1 # DW_AT_decl_file
.byte 2 # DW_AT_decl_line
.byte 2 # DW_AT_location
.byte 251
.byte 0
.byte 2 # DW_AT_linkage_name
.byte 3 # Abbrev [3] 0x25:0x4 DW_TAG_base_type
.byte 1 # DW_AT_name
.byte 5 # DW_AT_encoding
.byte 4 # DW_AT_byte_size
.byte 4 # Abbrev [4] 0x29:0x1c DW_TAG_subprogram
.byte 1 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
.byte 1 # DW_AT_frame_base
.byte 86
.byte 3 # DW_AT_linkage_name
.byte 4 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 3 # DW_AT_decl_line
.long 37 # DW_AT_type
# DW_AT_external
.byte 5 # Abbrev [5] 0x39:0xb DW_TAG_formal_parameter
.byte 2 # DW_AT_location
.byte 145
.byte 124
.byte 9 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 3 # DW_AT_decl_line
.long 37 # DW_AT_type
.byte 0 # End Of Children Mark
.byte 4 # Abbrev [4] 0x45:0x1c DW_TAG_subprogram
.byte 2 # DW_AT_low_pc
.long .Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
.byte 1 # DW_AT_frame_base
.byte 86
.byte 5 # DW_AT_linkage_name
.byte 6 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 11 # DW_AT_decl_line
.long 37 # DW_AT_type
# DW_AT_external
.byte 5 # Abbrev [5] 0x55:0xb DW_TAG_formal_parameter
.byte 2 # DW_AT_location
.byte 145
.byte 124
.byte 9 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 11 # DW_AT_decl_line
.long 37 # DW_AT_type
.byte 0 # End Of Children Mark
.byte 4 # Abbrev [4] 0x61:0x1c DW_TAG_subprogram
.byte 3 # DW_AT_low_pc
.long .Lfunc_end2-.Lfunc_begin2 # DW_AT_high_pc
.byte 1 # DW_AT_frame_base
.byte 86
.byte 7 # DW_AT_linkage_name
.byte 8 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 15 # DW_AT_decl_line
.long 37 # DW_AT_type
# DW_AT_external
.byte 6 # Abbrev [6] 0x71:0xb DW_TAG_variable
.byte 2 # DW_AT_location
.byte 145
.byte 124
.byte 9 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 16 # DW_AT_decl_line
.long 37 # DW_AT_type
.byte 0 # End Of Children Mark
.byte 0 # End Of Children Mark
.Ldebug_info_dwo_end0:
.section .debug_abbrev.dwo,"e",@progbits
.byte 1 # Abbreviation Code
.byte 17 # DW_TAG_compile_unit
.byte 1 # DW_CHILDREN_yes
.byte 37 # DW_AT_producer
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 19 # DW_AT_language
.byte 5 # DW_FORM_data2
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.ascii "\260B" # DW_AT_GNU_dwo_name
.ascii "\202>" # DW_FORM_GNU_str_index
.ascii "\261B" # DW_AT_GNU_dwo_id
.byte 7 # DW_FORM_data8
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 110 # DW_AT_linkage_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 3 # Abbreviation Code
.byte 36 # DW_TAG_base_type
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 62 # DW_AT_encoding
.byte 11 # DW_FORM_data1
.byte 11 # DW_AT_byte_size
.byte 11 # DW_FORM_data1
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 4 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 1 # DW_CHILDREN_yes
.byte 17 # DW_AT_low_pc
.ascii "\201>" # DW_FORM_GNU_addr_index
.byte 18 # DW_AT_high_pc
.byte 6 # DW_FORM_data4
.byte 64 # DW_AT_frame_base
.byte 24 # DW_FORM_exprloc
.byte 110 # DW_AT_linkage_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 63 # DW_AT_external
.byte 25 # DW_FORM_flag_present
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 5 # Abbreviation Code
.byte 5 # DW_TAG_formal_parameter
.byte 0 # DW_CHILDREN_no
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 6 # Abbreviation Code
.byte 52 # DW_TAG_variable
.byte 0 # DW_CHILDREN_no
.byte 2 # DW_AT_location
.byte 24 # DW_FORM_exprloc
.byte 3 # DW_AT_name
.ascii "\202>" # DW_FORM_GNU_str_index
.byte 58 # DW_AT_decl_file
.byte 11 # DW_FORM_data1
.byte 59 # DW_AT_decl_line
.byte 11 # DW_FORM_data1
.byte 73 # DW_AT_type
.byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)
.section .debug_addr,"",@progbits
.Laddr_table_base0:
.quad _ZL3foo
.quad .Lfunc_begin0
.quad .Lfunc_begin1
.quad .Lfunc_begin2
.section .debug_gnu_pubnames,"",@progbits
.long .LpubNames_end0-.LpubNames_start0 # Length of Public Names Info
.LpubNames_start0:
.short 2 # DWARF Version
.long .Lcu_begin0 # Offset of Compilation Unit Info
.long 48 # Compilation Unit Length
.long 25 # DIE offset
.byte 160 # Attributes: VARIABLE, STATIC
.asciz "foo" # External Name
.long 41 # DIE offset
.byte 48 # Attributes: FUNCTION, EXTERNAL
.asciz "doStuff" # External Name
.long 69 # DIE offset
.byte 48 # Attributes: FUNCTION, EXTERNAL
.asciz "doStuff2" # External Name
.long 97 # DIE offset
.byte 48 # Attributes: FUNCTION, EXTERNAL
.asciz "_start" # External Name
.long 0 # End Mark
.LpubNames_end0:
.section .debug_gnu_pubtypes,"",@progbits
.long .LpubTypes_end0-.LpubTypes_start0 # Length of Public Types Info
.LpubTypes_start0:
.short 2 # DWARF Version
.long .Lcu_begin0 # Offset of Compilation Unit Info
.long 48 # Compilation Unit Length
.long 37 # DIE offset
.byte 144 # Attributes: TYPE, STATIC
.asciz "int" # External Name
.long 0 # End Mark
.LpubTypes_end0:
.ident "clang version 13"
.section ".note.GNU-stack","",@progbits
.addrsig
.addrsig_sym _Z7doStuffi
.addrsig_sym _ZL3foo
.section .debug_line,"",@progbits
.Lline_table_start0:

View File

@@ -0,0 +1,40 @@
# REQUIRES: system-linux
# RUN: mkdir -p %t.dir && cd %t.dir
# RUN: cp %S/Inputs/debug-fission-simple.s debug-fission-simple.s
# RUN: cp %S/Inputs/debug-fission-script.txt debug-fission-script.txt
# RUN: llvm-mc -g -filetype=obj -triple x86_64-unknown-unknown --split-dwarf-file=debug-fission-simple.dwo \
# RUN: ./debug-fission-simple.s -o ./debug-fission-simple.o
# RUN: %host_cxx %cxxflags -g -Wl,--gc-sections,-q,-nostdlib -Wl,--undefined=_Z6_startv -nostartfiles -Wl,--script=debug-fission-script.txt ./debug-fission-simple.o -o out.exe
# RUN: llvm-bolt out.exe --reorder-blocks=reverse -update-debug-sections -dwo-output-path=%t.dir -o out.bolt
# RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.dir/debug-fission-simple.dwo0.dwo | grep DW_FORM_GNU_addr_index | FileCheck %s --check-prefix=CHECK-ADDR-INDEX
# RUN: llvm-dwarfdump --show-form --verbose --debug-addr out.bolt | FileCheck %s --check-prefix=CHECK-ADDR-SEC
# CHECK-ADDR-INDEX: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000001)
# CHECK-ADDR-INDEX: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000002)
# CHECK-ADDR-INDEX: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000003)
# CHECK-ADDR-SEC: .debug_addr contents:
# CHECK-ADDR-SEC: 0x00000000: Addrs: [
# CHECK-ADDR-SEC: 0x0000000000601000
# CHECK-ADDR-SEC: 0x0000000000a00000
# CHECK-ADDR-SEC: 0x0000000000000000
# CHECK-ADDR-SEC: 0x0000000000a00040
//clang++ -ffunction-sections -fno-exceptions -g -gsplit-dwarf=split -S debug-fission-simple.cpp -o debug-fission-simple.s
static int foo = 2;
int doStuff(int val) {
if (val == 5)
val += 1 + foo;
else
val -= 1;
return val;
}
int doStuff2(int val) {
return val += 3;
}
int main(int argc, const char** argv) {
return doStuff(argc);
}